[skip ci] Update dependencies (#1343)
* Bump harmless dependencies Note that bumping the android plugin will make compilation show lots of "hey bro use namespace instead of AndroidManifest.xml" warnings * Remove duktape dependency Zero extensions are using it, so its safe to remove. For executing js in extensions we can use quickjs instead of ducktape. * Upgrade gradle to 7.6 * Update kotlin * Update OkHttp * Update JSoup Jesus Christ this was boring asf * Update KtLint * [skip ci] refactor on some build.gradle.kts files * Expose coroutines to all extension by default
This commit is contained in:
parent
a5c2427e70
commit
62f45e094d
@ -8,3 +8,5 @@ indent_size = 4
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
@ -1,23 +1,8 @@
|
|||||||
buildscript {
|
plugins {
|
||||||
repositories {
|
alias(libs.plugins.android.application) apply false
|
||||||
mavenCentral()
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
google()
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
maven(url = "https://plugins.gradle.org/m2/")
|
alias(libs.plugins.kotlinter) apply false
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath(libs.gradle.agp)
|
|
||||||
classpath(libs.gradle.kotlin)
|
|
||||||
classpath(libs.gradle.serialization)
|
|
||||||
classpath(libs.gradle.kotlinter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
google()
|
|
||||||
maven(url = "https://jitpack.io")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Delete>("clean") {
|
tasks.register<Delete>("clean") {
|
||||||
|
@ -79,12 +79,16 @@ android {
|
|||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
freeCompilerArgs += "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
kotlinter {
|
||||||
mavenCentral()
|
experimentalRules = true
|
||||||
|
disabledRules = [
|
||||||
|
"experimental:argument-list-wrapping", // Doesn't play well with Android Studio
|
||||||
|
"experimental:comment-wrapping",
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -92,5 +96,15 @@ dependencies {
|
|||||||
compileOnly(libs.bundles.common)
|
compileOnly(libs.bundles.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||||
|
if (details.requested.group == 'org.jetbrains.kotlin' && details.requested.name == 'kotlin-stdlib-jdk8' && details.requested.version == '1.7.0') {
|
||||||
|
details.useVersion '1.7.21'
|
||||||
|
details.because 'Fix problems with dev.datlag JsUnpacker'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
preBuild.dependsOn(lintKotlin)
|
preBuild.dependsOn(lintKotlin)
|
||||||
lintKotlin.dependsOn(formatKotlin)
|
lintKotlin.dependsOn(formatKotlin)
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin_version = "1.6.21"
|
agp_version = "7.4.1"
|
||||||
coroutines_version = "1.6.0"
|
coroutines_version = "1.6.4"
|
||||||
serialization_version = "1.3.2"
|
kotlin_version = "1.7.21"
|
||||||
|
serialization_version = "1.4.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
gradle-agp = { module = "com.android.tools.build:gradle", version = "7.2.1" }
|
|
||||||
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
|
|
||||||
gradle-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin_version" }
|
|
||||||
gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version = "3.6.0" }
|
|
||||||
|
|
||||||
aniyomi-lib = { module = "com.github.jmir1:extensions-lib", version = "a2f1874" }
|
aniyomi-lib = { module = "com.github.jmir1:extensions-lib", version = "a2f1874" }
|
||||||
|
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin_version" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin_version" }
|
||||||
@ -19,14 +15,19 @@ coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", ve
|
|||||||
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" }
|
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" }
|
||||||
|
|
||||||
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
|
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
|
||||||
jsoup = { module = "org.jsoup:jsoup", version = "1.13.1" }
|
|
||||||
duktape = { module = "com.squareup.duktape:duktape-android", version = "1.4.0" }
|
|
||||||
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
|
|
||||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "4.9.1" }
|
|
||||||
rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" }
|
rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" }
|
||||||
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
|
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
|
||||||
|
jsoup = { module = "org.jsoup:jsoup", version = "1.15.3" }
|
||||||
|
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }
|
||||||
|
quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
common = ["kotlin-stdlib", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "aniyomi-lib", "duktape", "quickjs"]
|
common = ["kotlin-stdlib", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "aniyomi-lib", "quickjs", "coroutines-core", "coroutines-android"]
|
||||||
coroutines = ["coroutines-core", "coroutines-android"]
|
|
||||||
reactivex = ["rxandroid"]
|
reactivex = ["rxandroid"]
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp_version" }
|
||||||
|
android-library = { id = "com.android.library", version.ref = "agp_version" }
|
||||||
|
kotlinter = { id = "org.jmailen.kotlinter", version = "3.13.0" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" }
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
18
gradlew
vendored
18
gradlew
vendored
@ -55,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/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -80,10 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# 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"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
@ -143,12 +143,16 @@ fi
|
|||||||
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=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=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
|
||||||
@ -205,6 +209,12 @@ set -- \
|
|||||||
org.gradle.wrapper.GradleWrapperMain \
|
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.
|
||||||
|
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@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
|
||||||
@ -25,7 +25,8 @@
|
|||||||
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%
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ 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%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
: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%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 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!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=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
|
||||||
|
@ -12,10 +12,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
|
@ -12,13 +12,9 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -19,7 +19,7 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
val newUrl = if(redirect) response.request.url.toString() else url
|
val newUrl = if(redirect) response.request.url.toString() else url
|
||||||
|
|
||||||
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
|
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
|
||||||
val content = response.body!!.string()
|
val content = response.body.string()
|
||||||
if (!content.contains("'/pass_md5/")) return null
|
if (!content.contains("'/pass_md5/")) return null
|
||||||
val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
|
val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
|
||||||
val token = md5.substringAfterLast("/")
|
val token = md5.substringAfterLast("/")
|
||||||
@ -30,7 +30,7 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
"https://dood.$doodTld/pass_md5/$md5",
|
"https://dood.$doodTld/pass_md5/$md5",
|
||||||
Headers.headersOf("referer", newUrl)
|
Headers.headersOf("referer", newUrl)
|
||||||
)
|
)
|
||||||
).execute().body!!.string()
|
).execute().body.string()
|
||||||
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
|
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
|
||||||
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld))
|
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -13,14 +13,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.kotlin.json)
|
compileOnly(libs.kotlin.json)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -16,7 +16,7 @@ class FembedExtractor(private val client: OkHttpClient) {
|
|||||||
url.replace("/v/", "/api/source/")
|
url.replace("/v/", "/api/source/")
|
||||||
}
|
}
|
||||||
val body = runCatching {
|
val body = runCatching {
|
||||||
client.newCall(POST(videoApi)).execute().body?.string().orEmpty()
|
client.newCall(POST(videoApi)).execute().body.string()
|
||||||
}.getOrNull() ?: return emptyList<Video>()
|
}.getOrNull() ?: return emptyList<Video>()
|
||||||
|
|
||||||
val jsonResponse = try{ Json { ignoreUnknownKeys = true }.decodeFromString<FembedResponse>(body) } catch (e: Exception) { FembedResponse(false, emptyList()) }
|
val jsonResponse = try{ Json { ignoreUnknownKeys = true }.decodeFromString<FembedResponse>(body) } catch (e: Exception) { FembedResponse(false, emptyList()) }
|
||||||
|
@ -12,14 +12,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
compileOnly(libs.jsoup)
|
compileOnly(libs.jsoup)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -12,14 +12,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.kotlin.json)
|
compileOnly(libs.kotlin.json)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -52,12 +52,12 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
val master = fixUrl(url, common)
|
val master = fixUrl(url, common)
|
||||||
val json = Json.decodeFromString<JsonObject>(
|
val json = Json.decodeFromString<JsonObject>(
|
||||||
client.newCall(GET(master, newHeaders))
|
client.newCall(GET(master, newHeaders))
|
||||||
.execute().body!!.string()
|
.execute().body.string()
|
||||||
)
|
)
|
||||||
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
|
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
|
||||||
.execute()
|
.execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
val separator = "#EXT-X-STREAM-INF"
|
val separator = "#EXT-X-STREAM-INF"
|
||||||
masterPlaylist.substringAfter(separator).split(separator).map {
|
masterPlaylist.substringAfter(separator).split(separator).map {
|
||||||
val resolution = it.substringAfter("RESOLUTION=")
|
val resolution = it.substringAfter("RESOLUTION=")
|
||||||
@ -81,9 +81,9 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
|
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
|
||||||
return try {
|
return try {
|
||||||
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body!!.string())
|
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body.string())
|
||||||
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body.string()
|
||||||
val separator = "#EXT-X-STREAM-INF"
|
val separator = "#EXT-X-STREAM-INF"
|
||||||
masterPlaylist.substringAfter(separator).split(separator).map {
|
masterPlaylist.substringAfter(separator).split(separator).map {
|
||||||
val resolution = it.substringAfter("RESOLUTION=")
|
val resolution = it.substringAfter("RESOLUTION=")
|
||||||
|
@ -12,14 +12,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
compileOnly(libs.jsoup)
|
compileOnly(libs.jsoup)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -12,14 +12,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
compileOnly(libs.aniyomi.lib)
|
compileOnly(libs.aniyomi.lib)
|
||||||
compileOnly(libs.jsoup)
|
compileOnly(libs.jsoup)
|
||||||
}
|
}
|
||||||
// BUMPS: 0
|
// BUMPS: 0
|
||||||
|
@ -13,10 +13,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.kotlin.stdlib)
|
compileOnly(libs.kotlin.stdlib)
|
||||||
compileOnly(libs.okhttp)
|
compileOnly(libs.okhttp)
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
dependencyResolutionManagement {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
maven(url = "https://jitpack.io")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
include(":core")
|
include(":core")
|
||||||
|
|
||||||
File(rootDir, "lib").eachDir {
|
File(rootDir, "lib").eachDir {
|
||||||
|
@ -33,8 +33,8 @@ class AOAPIInterceptor(client: OkHttpClient) : Interceptor {
|
|||||||
"https://auth.animeonsen.xyz/oauth/token",
|
"https://auth.animeonsen.xyz/oauth/token",
|
||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
)
|
),
|
||||||
).execute().body!!.string()
|
).execute().body.string()
|
||||||
|
|
||||||
val tokenObject = Json.decodeFromString<JsonObject>(tokenResponse)
|
val tokenObject = Json.decodeFromString<JsonObject>(tokenResponse)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
GET("$apiUrl/content/index?start=${(page - 1) * 20}&limit=20")
|
GET("$apiUrl/content/index?start=${(page - 1) * 20}&limit=20")
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
val responseJson = json.decodeFromString<AnimeListResponse>(response.body!!.string())
|
val responseJson = json.decodeFromString<AnimeListResponse>(response.body.string())
|
||||||
val animes = responseJson.content.map {
|
val animes = responseJson.content.map {
|
||||||
it.toSAnime()
|
it.toSAnime()
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val contentId = response.request.url.toString().substringBeforeLast("/episodes")
|
val contentId = response.request.url.toString().substringBeforeLast("/episodes")
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
val responseJson = json.decodeFromString<JsonObject>(response.body!!.string())
|
val responseJson = json.decodeFromString<JsonObject>(response.body.string())
|
||||||
return responseJson.keys.map { epNum ->
|
return responseJson.keys.map { epNum ->
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
url = "$contentId/video/$epNum"
|
url = "$contentId/video/$epNum"
|
||||||
@ -100,12 +100,14 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val videoData = json.decodeFromString<VideoData>(response.body!!.string())
|
val videoData = json.decodeFromString<VideoData>(response.body.string())
|
||||||
val videoUrl = videoData.uri.stream
|
val videoUrl = videoData.uri.stream
|
||||||
val subtitleLangs = videoData.metadata.subtitles
|
val subtitleLangs = videoData.metadata.subtitles
|
||||||
val headers = Headers.headersOf(
|
val headers = Headers.headersOf(
|
||||||
"referer", baseUrl,
|
"referer",
|
||||||
"user-agent", AO_USER_AGENT,
|
baseUrl,
|
||||||
|
"user-agent",
|
||||||
|
AO_USER_AGENT,
|
||||||
)
|
)
|
||||||
val video = try {
|
val video = try {
|
||||||
val subtitles = videoData.uri.subtitles.keys.toList().sortSubs().map {
|
val subtitles = videoData.uri.subtitles.keys.toList().sortSubs().map {
|
||||||
@ -126,7 +128,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
// =============================== Search ===============================
|
// =============================== Search ===============================
|
||||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
val searchResult = json.decodeFromString<SearchResponse>(response.body!!.string()).result
|
val searchResult = json.decodeFromString<SearchResponse>(response.body.string()).result
|
||||||
val results = searchResult.map {
|
val results = searchResult.map {
|
||||||
it.toSAnime()
|
it.toSAnime()
|
||||||
}
|
}
|
||||||
@ -137,7 +139,7 @@ class AnimeOnsen : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
// =========================== Anime Details ============================
|
// =========================== Anime Details ============================
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val details = json.decodeFromString<AnimeDetails>(response.body!!.string())
|
val details = json.decodeFromString<AnimeDetails>(response.body.string())
|
||||||
val anime = SAnime.create().apply {
|
val anime = SAnime.create().apply {
|
||||||
url = details.content_id
|
url = details.content_id
|
||||||
title = details.content_title ?: details.content_title_en!!
|
title = details.content_title ?: details.content_title_en!!
|
||||||
@ -223,10 +225,10 @@ private const val PREF_SUB_TITLE = "Preferred sub language"
|
|||||||
private val PREF_SUB_ENTRIES = arrayOf(
|
private val PREF_SUB_ENTRIES = arrayOf(
|
||||||
"العربية", "Deutsch", "English", "Español (Spain)",
|
"العربية", "Deutsch", "English", "Español (Spain)",
|
||||||
"Español (Latin)", "Français", "Italiano",
|
"Español (Latin)", "Français", "Italiano",
|
||||||
"Português (Brasil)", "Русский"
|
"Português (Brasil)", "Русский",
|
||||||
)
|
)
|
||||||
private val PREF_SUB_VALUES = arrayOf(
|
private val PREF_SUB_VALUES = arrayOf(
|
||||||
"ar-ME", "de-DE", "en-US", "es-ES",
|
"ar-ME", "de-DE", "en-US", "es-ES",
|
||||||
"es-LA", "fr-FR", "it-IT",
|
"es-LA", "fr-FR", "it-IT",
|
||||||
"pt-BR", "ru-RU"
|
"pt-BR", "ru-RU",
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,7 @@ import kotlinx.serialization.json.JsonObject
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class AnimeListResponse(
|
data class AnimeListResponse(
|
||||||
val content: List<AnimeListItem>,
|
val content: List<AnimeListItem>,
|
||||||
val cursor: AnimeListCursor
|
val cursor: AnimeListCursor,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -19,7 +19,7 @@ data class AnimeListItem(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AnimeListCursor(
|
data class AnimeListCursor(
|
||||||
val next: JsonArray
|
val next: JsonArray,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -11,7 +11,6 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly libs.bundles.coroutines
|
|
||||||
implementation(project(':lib-okru-extractor'))
|
implementation(project(':lib-okru-extractor'))
|
||||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
setUrlWithoutDomain(element.selectFirst("a.series").attr("href").toHttpUrl().encodedPath)
|
setUrlWithoutDomain(element.selectFirst("a.series")!!.attr("href").toHttpUrl().encodedPath)
|
||||||
thumbnail_url = element.selectFirst("img").attr("src").substringBefore("?resize")
|
thumbnail_url = element.selectFirst("img")!!.attr("src").substringBefore("?resize")
|
||||||
title = element.selectFirst("a.series:not(:has(img))").text()
|
title = element.selectFirst("a.series:not(:has(img))")!!.text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,9 +120,9 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
setUrlWithoutDomain(element.selectFirst("a").attr("href").toHttpUrl().encodedPath)
|
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
|
||||||
thumbnail_url = element.selectFirst("img").attr("src").substringBefore("?resize")
|
thumbnail_url = element.selectFirst("img")!!.attr("src").substringBefore("?resize")
|
||||||
title = element.selectFirst("div.tt").text()
|
title = element.selectFirst("div.tt")!!.text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +132,8 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
return SAnime.create().apply {
|
return SAnime.create().apply {
|
||||||
title = document.selectFirst("h1.entry-title").text()
|
title = document.selectFirst("h1.entry-title")!!.text()
|
||||||
thumbnail_url = document.selectFirst("div.thumb > img").attr("src").substringBefore("?resize")
|
thumbnail_url = document.selectFirst("div.thumb > img")!!.attr("src").substringBefore("?resize")
|
||||||
status = SAnime.COMPLETED
|
status = SAnime.COMPLETED
|
||||||
description = document.select("div[itemprop=description] p")?.let {
|
description = document.select("div[itemprop=description] p")?.let {
|
||||||
it.joinToString("\n\n") { t -> t.text() } +
|
it.joinToString("\n\n") { t -> t.text() } +
|
||||||
@ -151,7 +151,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
return document.select("div.eplister > ul > li").map { episodeElement ->
|
return document.select("div.eplister > ul > li").map { episodeElement ->
|
||||||
val numberText = episodeElement.selectFirst("div.epl-num").text()
|
val numberText = episodeElement.selectFirst("div.epl-num")!!.text()
|
||||||
val numberString = numberText.substringBefore(" ")
|
val numberString = numberText.substringBefore(" ")
|
||||||
val episodeNumber = if (numberText.contains("part 2", true)) {
|
val episodeNumber = if (numberText.contains("part 2", true)) {
|
||||||
numberString.toFloatOrNull()?.plus(0.5F) ?: 0F
|
numberString.toFloatOrNull()?.plus(0.5F) ?: 0F
|
||||||
@ -163,7 +163,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
episode_number = episodeNumber
|
episode_number = episodeNumber
|
||||||
name = numberText
|
name = numberText
|
||||||
date_upload = parseDate(episodeElement.selectFirst("div.epl-date")?.text() ?: "")
|
date_upload = parseDate(episodeElement.selectFirst("div.epl-date")?.text() ?: "")
|
||||||
setUrlWithoutDomain(episodeElement.selectFirst("a").attr("href").toHttpUrl().encodedPath)
|
setUrlWithoutDomain(episodeElement.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
document.select("select.mirror > option[value~=.]").parallelMap { source ->
|
document.select("select.mirror > option[value~=.]").parallelMap { source ->
|
||||||
runCatching {
|
runCatching {
|
||||||
var decoded = Jsoup.parse(
|
var decoded = Jsoup.parse(
|
||||||
String(Base64.decode(source.attr("value"), Base64.DEFAULT))
|
String(Base64.decode(source.attr("value"), Base64.DEFAULT)),
|
||||||
).select("iframe[src~=.]").attr("src")
|
).select("iframe[src~=.]").attr("src")
|
||||||
if (!decoded.startsWith("http")) decoded = "https:$decoded"
|
if (!decoded.startsWith("http")) decoded = "https:$decoded"
|
||||||
val prefix = "${source.text()} - "
|
val prefix = "${source.text()} - "
|
||||||
@ -246,7 +246,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}.filterNotNull().flatten()
|
}.filterNotNull().flatten(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return videoList.sort()
|
return videoList.sort()
|
||||||
@ -268,7 +268,7 @@ class AnimeXin : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
compareBy(
|
compareBy(
|
||||||
{ it.quality.contains(quality) },
|
{ it.quality.contains(quality) },
|
||||||
{ it.quality.contains(language, true) },
|
{ it.quality.contains(language, true) },
|
||||||
)
|
),
|
||||||
).reversed()
|
).reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ object AnimeXinFilters {
|
|||||||
|
|
||||||
open class QueryPartFilter(
|
open class QueryPartFilter(
|
||||||
displayName: String,
|
displayName: String,
|
||||||
val vals: Array<Pair<String, String>>
|
val vals: Array<Pair<String, String>>,
|
||||||
) : AnimeFilter.Select<String>(
|
) : AnimeFilter.Select<String>(
|
||||||
displayName,
|
displayName,
|
||||||
vals.map { it.first }.toTypedArray()
|
vals.map { it.first }.toTypedArray(),
|
||||||
) {
|
) {
|
||||||
fun toQueryPart() = vals[state].second
|
fun toQueryPart() = vals[state].second
|
||||||
}
|
}
|
||||||
@ -33,28 +33,33 @@ object AnimeXinFilters {
|
|||||||
): String {
|
): String {
|
||||||
return (this.getFirst<R>() as CheckBoxFilterList).state
|
return (this.getFirst<R>() as CheckBoxFilterList).state
|
||||||
.mapNotNull { checkbox ->
|
.mapNotNull { checkbox ->
|
||||||
if (checkbox.state)
|
if (checkbox.state) {
|
||||||
options.find { it.first == checkbox.name }!!.second
|
options.find { it.first == checkbox.name }!!.second
|
||||||
else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}.joinToString("&$name[]=").let {
|
}.joinToString("&$name[]=").let {
|
||||||
if (it.isBlank()) ""
|
if (it.isBlank()) {
|
||||||
else "$name[]=$it"
|
""
|
||||||
|
} else {
|
||||||
|
"$name[]=$it"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenresFilter : CheckBoxFilterList(
|
class GenresFilter : CheckBoxFilterList(
|
||||||
"Genres",
|
"Genres",
|
||||||
AnimeXinFiltersData.genres.map { CheckBoxVal(it.first, false) }
|
AnimeXinFiltersData.genres.map { CheckBoxVal(it.first, false) },
|
||||||
)
|
)
|
||||||
|
|
||||||
class SeasonsFilter : CheckBoxFilterList(
|
class SeasonsFilter : CheckBoxFilterList(
|
||||||
"Seasons",
|
"Seasons",
|
||||||
AnimeXinFiltersData.seasons.map { CheckBoxVal(it.first, false) }
|
AnimeXinFiltersData.seasons.map { CheckBoxVal(it.first, false) },
|
||||||
)
|
)
|
||||||
|
|
||||||
class StudiosFilter : CheckBoxFilterList(
|
class StudiosFilter : CheckBoxFilterList(
|
||||||
"Studios",
|
"Studios",
|
||||||
AnimeXinFiltersData.studios.map { CheckBoxVal(it.first, false) }
|
AnimeXinFiltersData.studios.map { CheckBoxVal(it.first, false) },
|
||||||
)
|
)
|
||||||
|
|
||||||
class StatusFilter : QueryPartFilter("Status", AnimeXinFiltersData.status)
|
class StatusFilter : QueryPartFilter("Status", AnimeXinFiltersData.status)
|
||||||
@ -80,7 +85,7 @@ object AnimeXinFilters {
|
|||||||
val status: String = "",
|
val status: String = "",
|
||||||
val type: String = "",
|
val type: String = "",
|
||||||
val sub: String = "",
|
val sub: String = "",
|
||||||
val order: String = ""
|
val order: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
||||||
@ -119,7 +124,7 @@ object AnimeXinFilters {
|
|||||||
Pair("School", "school"),
|
Pair("School", "school"),
|
||||||
Pair("Sci-fi", "sci-fi"),
|
Pair("Sci-fi", "sci-fi"),
|
||||||
Pair("Supernatural", "supernatural"),
|
Pair("Supernatural", "supernatural"),
|
||||||
Pair("War", "war")
|
Pair("War", "war"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val seasons = arrayOf(
|
val seasons = arrayOf(
|
||||||
@ -133,7 +138,7 @@ object AnimeXinFilters {
|
|||||||
Pair("Season 7", "season-7"),
|
Pair("Season 7", "season-7"),
|
||||||
Pair("Season 8", "season-8"),
|
Pair("Season 8", "season-8"),
|
||||||
Pair("season1", "season1"),
|
Pair("season1", "season1"),
|
||||||
Pair("Winter 2023", "winter-2023")
|
Pair("Winter 2023", "winter-2023"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val studios = arrayOf(
|
val studios = arrayOf(
|
||||||
@ -178,7 +183,7 @@ object AnimeXinFilters {
|
|||||||
Pair("Wonder Cat Animation", "wonder-cat-animation"),
|
Pair("Wonder Cat Animation", "wonder-cat-animation"),
|
||||||
Pair("Xing Yi Kai Chen", "xing-yi-kai-chen"),
|
Pair("Xing Yi Kai Chen", "xing-yi-kai-chen"),
|
||||||
Pair("Xuan Yuan", "xuan-yuan"),
|
Pair("Xuan Yuan", "xuan-yuan"),
|
||||||
Pair("Year Young Culture", "year-young-culture")
|
Pair("Year Young Culture", "year-young-culture"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val status = arrayOf(
|
val status = arrayOf(
|
||||||
@ -198,7 +203,7 @@ object AnimeXinFilters {
|
|||||||
Pair("Special", "special"),
|
Pair("Special", "special"),
|
||||||
Pair("BD", "bd"),
|
Pair("BD", "bd"),
|
||||||
Pair("ONA", "ona"),
|
Pair("ONA", "ona"),
|
||||||
Pair("Music", "music")
|
Pair("Music", "music"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val sub = arrayOf(
|
val sub = arrayOf(
|
||||||
|
@ -13,27 +13,27 @@ import uy.kohesive.injekt.injectLazy
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class DailyQuality(
|
data class DailyQuality(
|
||||||
val qualities: Auto,
|
val qualities: Auto,
|
||||||
val subtitles: Subtitle? = null
|
val subtitles: Subtitle? = null,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Auto(
|
data class Auto(
|
||||||
val auto: List<Item>
|
val auto: List<Item>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Item(
|
data class Item(
|
||||||
val type: String,
|
val type: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Subtitle(
|
data class Subtitle(
|
||||||
val data: Map<String, SubtitleObject>
|
val data: Map<String, SubtitleObject>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SubtitleObject(
|
data class SubtitleObject(
|
||||||
val label: String,
|
val label: String,
|
||||||
val urls: List<String>
|
val urls: List<String>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ class DailymotionExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromUrl(url: String, prefix: String): List<Video> {
|
fun videosFromUrl(url: String, prefix: String): List<Video> {
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val htmlString = client.newCall(GET(url)).execute().body!!.string()
|
val htmlString = client.newCall(GET(url)).execute().body.string()
|
||||||
|
|
||||||
val internalData = htmlString.substringAfter("\"dmInternalData\":").substringBefore("</script>")
|
val internalData = htmlString.substringAfter("\"dmInternalData\":").substringBefore("</script>")
|
||||||
val ts = internalData.substringAfter("\"ts\":").substringBefore(",")
|
val ts = internalData.substringAfter("\"ts\":").substringBefore(",")
|
||||||
@ -53,7 +53,7 @@ class DailymotionExtractor(private val client: OkHttpClient) {
|
|||||||
val jsonUrl = "https://www.dailymotion.com/player/metadata/video/${url.toHttpUrl().encodedPath}?locale=en-US&dmV1st=$v1st&dmTs=$ts&is_native_app=0"
|
val jsonUrl = "https://www.dailymotion.com/player/metadata/video/${url.toHttpUrl().encodedPath}?locale=en-US&dmV1st=$v1st&dmTs=$ts&is_native_app=0"
|
||||||
val parsed = json.decodeFromString<DailyQuality>(
|
val parsed = json.decodeFromString<DailyQuality>(
|
||||||
client.newCall(GET(jsonUrl))
|
client.newCall(GET(jsonUrl))
|
||||||
.execute().body!!.string()
|
.execute().body.string(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
@ -63,16 +63,16 @@ class DailymotionExtractor(private val client: OkHttpClient) {
|
|||||||
parsed.subtitles.data.map { k ->
|
parsed.subtitles.data.map { k ->
|
||||||
Track(
|
Track(
|
||||||
k.value.urls.first(),
|
k.value.urls.first(),
|
||||||
k.value.label
|
k.value.label,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
val masterUrl = parsed.qualities.auto.first().url
|
val masterUrl = parsed.qualities.auto.first().url
|
||||||
|
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
|
||||||
|
|
||||||
val separator = "#EXT-X-STREAM-INF"
|
val separator = "#EXT-X-STREAM-INF"
|
||||||
masterPlaylist.substringAfter(separator).split(separator).map {
|
masterPlaylist.substringAfter(separator).split(separator).map {
|
||||||
|
@ -11,7 +11,7 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
fun videoFromUrl(
|
fun videoFromUrl(
|
||||||
url: String,
|
url: String,
|
||||||
quality: String? = null,
|
quality: String? = null,
|
||||||
redirect: Boolean = true
|
redirect: Boolean = true,
|
||||||
): Video? {
|
): Video? {
|
||||||
val newQuality = quality ?: "Doodstream" + if (redirect) " mirror" else ""
|
val newQuality = quality ?: "Doodstream" + if (redirect) " mirror" else ""
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
val newUrl = if (redirect) response.request.url.toString() else url
|
val newUrl = if (redirect) response.request.url.toString() else url
|
||||||
|
|
||||||
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
|
val doodTld = newUrl.substringAfter("https://dood.").substringBefore("/")
|
||||||
val content = response.body!!.string()
|
val content = response.body.string()
|
||||||
|
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
val subtitleRegex = """src:'//(srt[^']*?)',\s*label:'([^']*?)'""".toRegex()
|
val subtitleRegex = """src:'//(srt[^']*?)',\s*label:'([^']*?)'""".toRegex()
|
||||||
@ -29,9 +29,9 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
subtitleRegex.findAll(content).map {
|
subtitleRegex.findAll(content).map {
|
||||||
Track(
|
Track(
|
||||||
"https://" + it.groupValues[1],
|
"https://" + it.groupValues[1],
|
||||||
it.groupValues[2]
|
it.groupValues[2],
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
|
|
||||||
@ -43,9 +43,9 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
val videoUrlStart = client.newCall(
|
val videoUrlStart = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
"https://dood.$doodTld/pass_md5/$md5",
|
"https://dood.$doodTld/pass_md5/$md5",
|
||||||
Headers.headersOf("referer", newUrl)
|
Headers.headersOf("referer", newUrl),
|
||||||
)
|
),
|
||||||
).execute().body!!.string()
|
).execute().body.string()
|
||||||
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
|
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
|
||||||
try {
|
try {
|
||||||
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld), subtitleTracks = subtitleList)
|
Video(newUrl, newQuality, videoUrl, headers = doodHeaders(doodTld), subtitleTracks = subtitleList)
|
||||||
@ -60,7 +60,7 @@ class DoodExtractor(private val client: OkHttpClient) {
|
|||||||
fun videosFromUrl(
|
fun videosFromUrl(
|
||||||
url: String,
|
url: String,
|
||||||
quality: String? = null,
|
quality: String? = null,
|
||||||
redirect: Boolean = true
|
redirect: Boolean = true,
|
||||||
): List<Video> {
|
): List<Video> {
|
||||||
val video = videoFromUrl(url, quality, redirect)
|
val video = videoFromUrl(url, quality, redirect)
|
||||||
return video?.let { listOf(it) } ?: emptyList<Video>()
|
return video?.let { listOf(it) } ?: emptyList<Video>()
|
||||||
|
@ -15,12 +15,12 @@ import okhttp3.OkHttpClient
|
|||||||
data class FembedResponse(
|
data class FembedResponse(
|
||||||
val success: Boolean,
|
val success: Boolean,
|
||||||
val data: List<FembedVideo> = emptyList(),
|
val data: List<FembedVideo> = emptyList(),
|
||||||
val captions: List<Caption> = emptyList()
|
val captions: List<Caption> = emptyList(),
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FembedVideo(
|
data class FembedVideo(
|
||||||
val file: String,
|
val file: String,
|
||||||
val label: String
|
val label: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -45,11 +45,11 @@ class FembedExtractor(private val client: OkHttpClient) {
|
|||||||
url.replace("/v/", "/api/source/")
|
url.replace("/v/", "/api/source/")
|
||||||
}
|
}
|
||||||
val body = runCatching {
|
val body = runCatching {
|
||||||
client.newCall(POST(videoApi)).execute().body?.string().orEmpty()
|
client.newCall(POST(videoApi)).execute().body.string()
|
||||||
}.getOrNull() ?: return emptyList()
|
}.getOrNull() ?: return emptyList()
|
||||||
|
|
||||||
val userId = client.newCall(GET(url)).execute().asJsoup()
|
val userId = client.newCall(GET(url)).execute().asJsoup()
|
||||||
.selectFirst("script:containsData(USER_ID)")
|
.selectFirst("script:containsData(USER_ID)")!!
|
||||||
.data()
|
.data()
|
||||||
.substringAfter("USER_ID")
|
.substringAfter("USER_ID")
|
||||||
.substringAfter("'")
|
.substringAfter("'")
|
||||||
@ -64,16 +64,19 @@ class FembedExtractor(private val client: OkHttpClient) {
|
|||||||
jsonResponse.captions.map {
|
jsonResponse.captions.map {
|
||||||
Track(
|
Track(
|
||||||
"https://${url.toHttpUrl().host}/asset/userdata/$userId/caption/${it.hash}/${it.id}.${it.extension}",
|
"https://${url.toHttpUrl().host}/asset/userdata/$userId/caption/${it.hash}/${it.id}.${it.extension}",
|
||||||
it.language
|
it.language,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
|
|
||||||
jsonResponse.data.map {
|
jsonResponse.data.map {
|
||||||
val quality = ("Fembed:${it.label}").let {
|
val quality = ("Fembed:${it.label}").let {
|
||||||
if (prefix.isNotBlank()) "$prefix $it"
|
if (prefix.isNotBlank()) {
|
||||||
else it
|
"$prefix $it"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Video(it.file, quality, it.file, subtitleTracks = subtitleList)
|
Video(it.file, quality, it.file, subtitleTracks = subtitleList)
|
||||||
|
@ -22,14 +22,18 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromUrl(url: String, name: String): List<Video> {
|
fun videosFromUrl(url: String, name: String): List<Video> {
|
||||||
val headers = Headers.headersOf(
|
val headers = Headers.headersOf(
|
||||||
"Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
"Accept",
|
||||||
"Host", "gdriveplayer.to",
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
"Referer", "https://animexin.vip/",
|
"Host",
|
||||||
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0"
|
"gdriveplayer.to",
|
||||||
|
"Referer",
|
||||||
|
"https://animexin.vip/",
|
||||||
|
"User-Agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
val body = client.newCall(GET(url.replace(".me", ".to"), headers = headers)).execute()
|
val body = client.newCall(GET(url.replace(".me", ".to"), headers = headers)).execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
|
|
||||||
val subtitleUrl = Jsoup.parse(body).selectFirst("div:contains(\\.srt)")
|
val subtitleUrl = Jsoup.parse(body).selectFirst("div:contains(\\.srt)")
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
@ -38,8 +42,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
subtitleList.add(
|
subtitleList.add(
|
||||||
Track(
|
Track(
|
||||||
"https://gdriveplayer.to/?subtitle=" + subtitleUrl.text(),
|
"https://gdriveplayer.to/?subtitle=" + subtitleUrl.text(),
|
||||||
"Subtitles"
|
"Subtitles",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
}
|
}
|
||||||
@ -89,9 +93,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
hashAlgorithm: String = "MD5",
|
hashAlgorithm: String = "MD5",
|
||||||
keyLength: Int = 32,
|
keyLength: Int = 32,
|
||||||
ivLength: Int = 16,
|
ivLength: Int = 16,
|
||||||
iterations: Int = 1
|
iterations: Int = 1,
|
||||||
): List<ByteArray>? {
|
): List<ByteArray>? {
|
||||||
|
|
||||||
val md = MessageDigest.getInstance(hashAlgorithm)
|
val md = MessageDigest.getInstance(hashAlgorithm)
|
||||||
val digestLength = md.getDigestLength()
|
val digestLength = md.getDigestLength()
|
||||||
val targetKeySize = keyLength + ivLength
|
val targetKeySize = keyLength + ivLength
|
||||||
@ -103,12 +106,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
md.reset()
|
md.reset()
|
||||||
|
|
||||||
while (generatedLength < targetKeySize) {
|
while (generatedLength < targetKeySize) {
|
||||||
if (generatedLength > 0)
|
if (generatedLength > 0) {
|
||||||
md.update(
|
md.update(
|
||||||
generatedData,
|
generatedData,
|
||||||
generatedLength - digestLength,
|
generatedLength - digestLength,
|
||||||
digestLength
|
digestLength,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
md.update(password)
|
md.update(password)
|
||||||
md.update(salt, 0, 8)
|
md.update(salt, 0, 8)
|
||||||
@ -123,7 +127,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
val result = listOf(
|
val result = listOf(
|
||||||
generatedData.copyOfRange(0, keyLength),
|
generatedData.copyOfRange(0, keyLength),
|
||||||
generatedData.copyOfRange(keyLength, targetKeySize)
|
generatedData.copyOfRange(keyLength, targetKeySize),
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
} catch (e: DigestException) {
|
} catch (e: DigestException) {
|
||||||
|
@ -50,7 +50,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
val master = fixUrl(url, common)
|
val master = fixUrl(url, common)
|
||||||
val json = Json.decodeFromString<JsonObject>(
|
val json = Json.decodeFromString<JsonObject>(
|
||||||
client.newCall(GET(master, newHeaders))
|
client.newCall(GET(master, newHeaders))
|
||||||
.execute().body!!.string()
|
.execute().body.string(),
|
||||||
)
|
)
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
val subsList = json["stream_data"]!!.jsonObject["subs"]
|
val subsList = json["stream_data"]!!.jsonObject["subs"]
|
||||||
@ -62,7 +62,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
it.jsonObject["file"]!!.jsonPrimitive.content,
|
it.jsonObject["file"]!!.jsonPrimitive.content,
|
||||||
it.jsonObject["label"]!!.jsonPrimitive.content,
|
it.jsonObject["label"]!!.jsonPrimitive.content,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
|
val masterPlaylist = client.newCall(GET(masterUrl, newHeaders))
|
||||||
.execute()
|
.execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
val separator = "#EXT-X-STREAM-INF"
|
val separator = "#EXT-X-STREAM-INF"
|
||||||
masterPlaylist.substringAfter(separator).split(separator).map {
|
masterPlaylist.substringAfter(separator).split(separator).map {
|
||||||
val resolution = it.substringAfter("RESOLUTION=")
|
val resolution = it.substringAfter("RESOLUTION=")
|
||||||
@ -78,11 +78,17 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
.substringAfter("x")
|
.substringAfter("x")
|
||||||
.substringBefore(",") + "p"
|
.substringBefore(",") + "p"
|
||||||
val quality = ("StreamSB:" + resolution).let {
|
val quality = ("StreamSB:" + resolution).let {
|
||||||
if (prefix.isNotBlank()) "$prefix $it"
|
if (prefix.isNotBlank()) {
|
||||||
else it
|
"$prefix $it"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}.let {
|
}.let {
|
||||||
if (suffix.isNotBlank()) "$it $suffix"
|
if (suffix.isNotBlank()) {
|
||||||
else it
|
"$it $suffix"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val videoUrl = it.substringAfter("\n").substringBefore("\n")
|
val videoUrl = it.substringAfter("\n").substringBefore("\n")
|
||||||
try {
|
try {
|
||||||
@ -98,9 +104,9 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
|
fun videosFromDecryptedUrl(realUrl: String, headers: Headers, prefix: String = "", suffix: String = ""): List<Video> {
|
||||||
return try {
|
return try {
|
||||||
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body!!.string())
|
val json = Json.decodeFromString<JsonObject>(client.newCall(GET(realUrl, headers)).execute().body.string())
|
||||||
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
val masterUrl = json["stream_data"]!!.jsonObject["file"].toString().trim('"')
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(masterUrl, headers)).execute().body.string()
|
||||||
val separator = "#EXT-X-STREAM-INF"
|
val separator = "#EXT-X-STREAM-INF"
|
||||||
masterPlaylist.substringAfter(separator).split(separator).map {
|
masterPlaylist.substringAfter(separator).split(separator).map {
|
||||||
val resolution = it.substringAfter("RESOLUTION=")
|
val resolution = it.substringAfter("RESOLUTION=")
|
||||||
@ -108,11 +114,17 @@ class StreamSBExtractor(private val client: OkHttpClient) {
|
|||||||
.substringAfter("x")
|
.substringAfter("x")
|
||||||
.substringBefore(",") + "p"
|
.substringBefore(",") + "p"
|
||||||
val quality = ("StreamSB:$resolution").let {
|
val quality = ("StreamSB:$resolution").let {
|
||||||
if (prefix.isNotBlank()) "$prefix $it"
|
if (prefix.isNotBlank()) {
|
||||||
else it
|
"$prefix $it"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}.let {
|
}.let {
|
||||||
if (suffix.isNotBlank()) "$it $suffix"
|
if (suffix.isNotBlank()) {
|
||||||
else it
|
"$it $suffix"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val videoUrl = it.substringAfter("\n").substringBefore("\n")
|
val videoUrl = it.substringAfter("\n").substringBefore("\n")
|
||||||
Video(videoUrl, quality, videoUrl, headers = headers)
|
Video(videoUrl, quality, videoUrl, headers = headers)
|
||||||
|
@ -41,7 +41,9 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
|
|||||||
val encryptAjaxParams = cryptoHandler(
|
val encryptAjaxParams = cryptoHandler(
|
||||||
document.select("script[data-value]")
|
document.select("script[data-value]")
|
||||||
.attr("data-value"),
|
.attr("data-value"),
|
||||||
iv, secretKey, false
|
iv,
|
||||||
|
secretKey,
|
||||||
|
false,
|
||||||
).substringAfter("&")
|
).substringAfter("&")
|
||||||
|
|
||||||
val httpUrl = serverUrl.toHttpUrl()
|
val httpUrl = serverUrl.toHttpUrl()
|
||||||
@ -55,10 +57,11 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
|
|||||||
GET(
|
GET(
|
||||||
"${host}encrypt-ajax.php?id=$encryptedId&$encryptAjaxParams&alias=$id",
|
"${host}encrypt-ajax.php?id=$encryptedId&$encryptAjaxParams&alias=$id",
|
||||||
Headers.headersOf(
|
Headers.headersOf(
|
||||||
"X-Requested-With", "XMLHttpRequest"
|
"X-Requested-With",
|
||||||
)
|
"XMLHttpRequest",
|
||||||
)
|
),
|
||||||
).execute().body!!.string()
|
),
|
||||||
|
).execute().body.string()
|
||||||
val data = json.decodeFromString<JsonObject>(jsonResponse)["data"]!!.jsonPrimitive.content
|
val data = json.decodeFromString<JsonObject>(jsonResponse)["data"]!!.jsonPrimitive.content
|
||||||
val decryptedData = cryptoHandler(data, iv, decryptionKey, false)
|
val decryptedData = cryptoHandler(data, iv, decryptionKey, false)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
@ -66,7 +69,7 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
|
|||||||
val array = json.decodeFromString<JsonObject>(decryptedData)["source"]!!.jsonArray
|
val array = json.decodeFromString<JsonObject>(decryptedData)["source"]!!.jsonArray
|
||||||
if (array.size == 1 && array[0].jsonObject["type"]!!.jsonPrimitive.content == "hls") {
|
if (array.size == 1 && array[0].jsonObject["type"]!!.jsonPrimitive.content == "hls") {
|
||||||
val fileURL = array[0].jsonObject["file"].toString().trim('"')
|
val fileURL = array[0].jsonObject["file"].toString().trim('"')
|
||||||
val masterPlaylist = client.newCall(GET(fileURL)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(fileURL)).execute().body.string()
|
||||||
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:")
|
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:")
|
||||||
.split("#EXT-X-STREAM-INF:").forEach {
|
.split("#EXT-X-STREAM-INF:").forEach {
|
||||||
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",").substringBefore("\n") + "p"
|
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",").substringBefore("\n") + "p"
|
||||||
@ -76,20 +79,25 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
videoList.add(Video(videoUrl, prefix + quality + qualitySuffix, videoUrl))
|
videoList.add(Video(videoUrl, prefix + quality + qualitySuffix, videoUrl))
|
||||||
}
|
}
|
||||||
} else array.forEach {
|
} else {
|
||||||
val label = it.jsonObject["label"].toString().lowercase(Locale.ROOT)
|
array.forEach {
|
||||||
.trim('"').replace(" ", "")
|
val label = it.jsonObject["label"].toString().lowercase(Locale.ROOT)
|
||||||
val fileURL = it.jsonObject["file"].toString().trim('"')
|
.trim('"').replace(" ", "")
|
||||||
val videoHeaders = Headers.headersOf("Referer", serverUrl)
|
val fileURL = it.jsonObject["file"].toString().trim('"')
|
||||||
if (label == "auto") autoList.add(
|
val videoHeaders = Headers.headersOf("Referer", serverUrl)
|
||||||
Video(
|
if (label == "auto") {
|
||||||
fileURL,
|
autoList.add(
|
||||||
label + qualitySuffix,
|
Video(
|
||||||
fileURL,
|
fileURL,
|
||||||
headers = videoHeaders
|
label + qualitySuffix,
|
||||||
)
|
fileURL,
|
||||||
)
|
headers = videoHeaders,
|
||||||
else videoList.add(Video(fileURL, label + qualitySuffix, fileURL, headers = videoHeaders))
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
videoList.add(Video(fileURL, label + qualitySuffix, fileURL, headers = videoHeaders))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return videoList.sortedByDescending {
|
return videoList.sortedByDescending {
|
||||||
it.quality.substringBefore(qualitySuffix).substringBefore("p").toIntOrNull() ?: -1
|
it.quality.substringBefore(qualitySuffix).substringBefore("p").toIntOrNull() ?: -1
|
||||||
@ -103,7 +111,7 @@ class VidstreamingExtractor(private val client: OkHttpClient) {
|
|||||||
string: String,
|
string: String,
|
||||||
iv: ByteArray,
|
iv: ByteArray,
|
||||||
secretKeyString: ByteArray,
|
secretKeyString: ByteArray,
|
||||||
encrypt: Boolean = true
|
encrypt: Boolean = true,
|
||||||
): String {
|
): String {
|
||||||
val ivParameterSpec = IvParameterSpec(iv)
|
val ivParameterSpec = IvParameterSpec(iv)
|
||||||
val secretKey = SecretKeySpec(secretKeyString, "AES")
|
val secretKey = SecretKeySpec(secretKeyString, "AES")
|
||||||
|
@ -34,7 +34,7 @@ class YouTubeExtractor(private val client: OkHttpClient) {
|
|||||||
val videoId = url.substringAfter("/embed/")
|
val videoId = url.substringAfter("/embed/")
|
||||||
|
|
||||||
val document = client.newCall(
|
val document = client.newCall(
|
||||||
GET(url.replace("/embed/", "/watch?v="))
|
GET(url.replace("/embed/", "/watch?v=")),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
|
|
||||||
for (element in document.select("script")) {
|
for (element in document.select("script")) {
|
||||||
@ -78,14 +78,14 @@ class YouTubeExtractor(private val client: OkHttpClient) {
|
|||||||
"X-YouTube-Client-Version", "17.31.35",
|
"X-YouTube-Client-Version", "17.31.35",
|
||||||
"Origin", "https://www.youtube.com",
|
"Origin", "https://www.youtube.com",
|
||||||
"User-Agent", "com.google.android.youtube/17.31.35 (Linux; U; Android 11) gzip",
|
"User-Agent", "com.google.android.youtube/17.31.35 (Linux; U; Android 11) gzip",
|
||||||
"content-type", "application/json"
|
"content-type", "application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
val postResponse = client.newCall(
|
val postResponse = client.newCall(
|
||||||
POST(playerUrl, headers = headers, body = body)
|
POST(playerUrl, headers = headers, body = body),
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
val responseObject = json.decodeFromString<JsonObject>(postResponse.body!!.string())
|
val responseObject = json.decodeFromString<JsonObject>(postResponse.body.string())
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
|
||||||
val formats = responseObject["streamingData"]!!
|
val formats = responseObject["streamingData"]!!
|
||||||
@ -103,8 +103,8 @@ class YouTubeExtractor(private val client: OkHttpClient) {
|
|||||||
Track(
|
Track(
|
||||||
format.jsonObject["url"]!!.jsonPrimitive.content,
|
format.jsonObject["url"]!!.jsonPrimitive.content,
|
||||||
format.jsonObject["audioQuality"]!!.jsonPrimitive.content +
|
format.jsonObject["audioQuality"]!!.jsonPrimitive.content +
|
||||||
" (${formatBits(format.jsonObject["averageBitrate"]!!.jsonPrimitive.long)}ps)"
|
" (${formatBits(format.jsonObject["averageBitrate"]!!.jsonPrimitive.long)}ps)",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
}
|
}
|
||||||
@ -124,8 +124,8 @@ class YouTubeExtractor(private val client: OkHttpClient) {
|
|||||||
Track(
|
Track(
|
||||||
// TODO: Would replacing srv3 with vtt work for every video?
|
// TODO: Would replacing srv3 with vtt work for every video?
|
||||||
captionJson["baseUrl"]!!.jsonPrimitive.content.replace("srv3", "vtt"),
|
captionJson["baseUrl"]!!.jsonPrimitive.content.replace("srv3", "vtt"),
|
||||||
captionJson["name"]!!.jsonObject["runs"]!!.jsonArray[0].jsonObject["text"]!!.jsonPrimitive.content
|
captionJson["name"]!!.jsonObject["runs"]!!.jsonArray[0].jsonObject["text"]!!.jsonPrimitive.content,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (a: Exception) { }
|
} catch (a: Exception) { }
|
||||||
}
|
}
|
||||||
@ -143,16 +143,16 @@ class YouTubeExtractor(private val client: OkHttpClient) {
|
|||||||
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
|
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
|
||||||
format.jsonObject["url"]!!.jsonPrimitive.content,
|
format.jsonObject["url"]!!.jsonPrimitive.content,
|
||||||
audioTracks = audioTracks,
|
audioTracks = audioTracks,
|
||||||
subtitleTracks = subtitleTracks
|
subtitleTracks = subtitleTracks,
|
||||||
)
|
)
|
||||||
} catch (a: Exception) {
|
} catch (a: Exception) {
|
||||||
Video(
|
Video(
|
||||||
format.jsonObject["url"]!!.jsonPrimitive.content,
|
format.jsonObject["url"]!!.jsonPrimitive.content,
|
||||||
prefix + format.jsonObject["qualityLabel"]!!.jsonPrimitive.content +
|
prefix + format.jsonObject["qualityLabel"]!!.jsonPrimitive.content +
|
||||||
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
|
" (${mimeType.substringAfter("codecs=\"").substringBefore("\"")})",
|
||||||
format.jsonObject["url"]!!.jsonPrimitive.content
|
format.jsonObject["url"]!!.jsonPrimitive.content,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -650,7 +650,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
val parsed = json.decodeFromString<AnilistResponse>(response.body!!.string())
|
val parsed = json.decodeFromString<AnilistResponse>(response.body.string())
|
||||||
|
|
||||||
val animeList = parsed.data.Page.media.map { ani ->
|
val animeList = parsed.data.Page.media.map { ani ->
|
||||||
SAnime.create().apply {
|
SAnime.create().apply {
|
||||||
@ -659,7 +659,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
author = ani.studios.nodes.firstOrNull()?.name ?: ""
|
author = ani.studios.nodes.firstOrNull()?.name ?: ""
|
||||||
genre = ani.genres.joinToString(", ")
|
genre = ani.genres.joinToString(", ")
|
||||||
description = Jsoup.parse(
|
description = Jsoup.parse(
|
||||||
ani.description.replace("<br>", "br2n")
|
ani.description.replace("<br>", "br2n"),
|
||||||
).text().replace("br2n", "\n")
|
).text().replace("br2n", "\n")
|
||||||
status = parseStatus(ani.status)
|
status = parseStatus(ani.status)
|
||||||
setUrlWithoutDomain(ani.id.toString())
|
setUrlWithoutDomain(ani.id.toString())
|
||||||
@ -734,7 +734,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val parsed = json.decodeFromString<EpisodeResponse>(response.body!!.string())
|
val parsed = json.decodeFromString<EpisodeResponse>(response.body.string())
|
||||||
val episodeList = mutableListOf<SEpisode>()
|
val episodeList = mutableListOf<SEpisode>()
|
||||||
|
|
||||||
if (parsed.episodes != null) {
|
if (parsed.episodes != null) {
|
||||||
@ -745,7 +745,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
episode_number = it.episodeNumber
|
episode_number = it.episodeNumber
|
||||||
setUrlWithoutDomain("/server/source?episode_id=${it.sourceEpisodeId}&source_media_id=${it.sourceMediaId}&source_id=${it.sourceId}")
|
setUrlWithoutDomain("/server/source?episode_id=${it.sourceEpisodeId}&source_media_id=${it.sourceMediaId}&source_id=${it.sourceId}")
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,19 +759,19 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
val audioList = mutableListOf<Track>()
|
val audioList = mutableListOf<Track>()
|
||||||
val sources = json.decodeFromString<SourcesResponse>(
|
val sources = json.decodeFromString<SourcesResponse>(
|
||||||
client.newCall(GET(baseUrl + episode.url)).execute().body!!.string()
|
client.newCall(GET(baseUrl + episode.url)).execute().body.string(),
|
||||||
)
|
)
|
||||||
subtitleList.addAll(
|
subtitleList.addAll(
|
||||||
sources.subtitles.map {
|
sources.subtitles.map {
|
||||||
Track(it.file, "${it.language} - ${it.lang}")
|
Track(it.file, "${it.language} - ${it.lang}")
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
sources.sources.forEach { source ->
|
sources.sources.forEach { source ->
|
||||||
if (source.type == "dash") {
|
if (source.type == "dash") {
|
||||||
// Parsing dash with Jsoup :YEP:
|
// Parsing dash with Jsoup :YEP:
|
||||||
val document = client.newCall(
|
val document = client.newCall(
|
||||||
GET(source.file)
|
GET(source.file),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
document.select("Representation[mimetype~=audio]").forEach { audioSrc ->
|
document.select("Representation[mimetype~=audio]").forEach { audioSrc ->
|
||||||
audioList.add(Track(audioSrc.text(), formatBits(audioSrc.attr("bandwidth").toLongOrNull() ?: 0L) ?: "audio"))
|
audioList.add(Track(audioSrc.text(), formatBits(audioSrc.attr("bandwidth").toLongOrNull() ?: 0L) ?: "audio"))
|
||||||
@ -783,8 +783,8 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
"${videoSrc.attr("height")}p - ${formatBits(videoSrc.attr("bandwidth").toLongOrNull() ?: 0L)}",
|
"${videoSrc.attr("height")}p - ${formatBits(videoSrc.attr("bandwidth").toLongOrNull() ?: 0L)}",
|
||||||
videoSrc.text(),
|
videoSrc.text(),
|
||||||
audioTracks = audioList,
|
audioTracks = audioList,
|
||||||
subtitleTracks = subtitleList
|
subtitleTracks = subtitleList,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -826,7 +826,7 @@ class ConsumyBili : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
compareBy(
|
compareBy(
|
||||||
{ it.quality.contains(quality) },
|
{ it.quality.contains(quality) },
|
||||||
{ it.quality.substringBefore("p ").toIntOrNull() ?: 0 },
|
{ it.quality.substringBefore("p ").toIntOrNull() ?: 0 },
|
||||||
)
|
),
|
||||||
).reversed()
|
).reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,20 +4,20 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AnilistResponse(
|
data class AnilistResponse(
|
||||||
val data: DataObject
|
val data: DataObject,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DataObject(
|
data class DataObject(
|
||||||
val Page: PageObject
|
val Page: PageObject,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PageObject(
|
data class PageObject(
|
||||||
val pageInfo: PageInfoObject,
|
val pageInfo: PageInfoObject,
|
||||||
val media: List<AnimeMedia>
|
val media: List<AnimeMedia>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PageInfoObject(
|
data class PageInfoObject(
|
||||||
val hasNextPage: Boolean
|
val hasNextPage: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -28,25 +28,25 @@ data class AnilistResponse(
|
|||||||
val studios: StudioNode,
|
val studios: StudioNode,
|
||||||
val genres: List<String>,
|
val genres: List<String>,
|
||||||
val description: String,
|
val description: String,
|
||||||
val status: String
|
val status: String,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TitleObject(
|
data class TitleObject(
|
||||||
val romaji: String
|
val romaji: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ImageObject(
|
data class ImageObject(
|
||||||
val extraLarge: String
|
val extraLarge: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class StudioNode(
|
data class StudioNode(
|
||||||
val nodes: List<Node>
|
val nodes: List<Node>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Node(
|
data class Node(
|
||||||
val name: String
|
val name: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ data class AnilistResponse(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeResponse(
|
data class EpisodeResponse(
|
||||||
val episodes: List<EpisodeObject>? = null
|
val episodes: List<EpisodeObject>? = null,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeObject(
|
data class EpisodeObject(
|
||||||
@ -70,18 +70,18 @@ data class EpisodeResponse(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class SourcesResponse(
|
data class SourcesResponse(
|
||||||
val sources: List<SourceObject>,
|
val sources: List<SourceObject>,
|
||||||
val subtitles: List<SubtitleObject>
|
val subtitles: List<SubtitleObject>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SourceObject(
|
data class SourceObject(
|
||||||
val file: String,
|
val file: String,
|
||||||
val type: String
|
val type: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SubtitleObject(
|
data class SubtitleObject(
|
||||||
val file: String,
|
val file: String,
|
||||||
val lang: String,
|
val lang: String,
|
||||||
val language: String
|
val language: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,4 @@ ext {
|
|||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly libs.bundles.coroutines
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class ItemsResponse(
|
data class ItemsResponse(
|
||||||
val TotalRecordCount: Int,
|
val TotalRecordCount: Int,
|
||||||
val Items: List<Item>
|
val Items: List<Item>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Item(
|
data class Item(
|
||||||
@ -24,7 +24,7 @@ data class ItemsResponse(
|
|||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ImageObject(
|
data class ImageObject(
|
||||||
val Primary: String? = null
|
val Primary: String? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ data class SessionResponse(
|
|||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MediaObject(
|
data class MediaObject(
|
||||||
val MediaStreams: List<MediaStream>
|
val MediaStreams: List<MediaStream>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MediaStream(
|
data class MediaStream(
|
||||||
@ -47,7 +47,7 @@ data class SessionResponse(
|
|||||||
val Language: String? = null,
|
val Language: String? = null,
|
||||||
val DisplayTitle: String? = null,
|
val DisplayTitle: String? = null,
|
||||||
val Height: Int? = null,
|
val Height: Int? = null,
|
||||||
val Width: Int? = null
|
val Width: Int? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,22 +17,28 @@ object JFConstants {
|
|||||||
const val HOSTURL_DEFAULT = "http://127.0.0.1:8096"
|
const val HOSTURL_DEFAULT = "http://127.0.0.1:8096"
|
||||||
|
|
||||||
fun getPrefApiKey(preferences: SharedPreferences): String? = preferences.getString(
|
fun getPrefApiKey(preferences: SharedPreferences): String? = preferences.getString(
|
||||||
APIKEY_KEY, null
|
APIKEY_KEY,
|
||||||
|
null,
|
||||||
)
|
)
|
||||||
fun getPrefUserId(preferences: SharedPreferences): String? = preferences.getString(
|
fun getPrefUserId(preferences: SharedPreferences): String? = preferences.getString(
|
||||||
USERID_KEY, null
|
USERID_KEY,
|
||||||
|
null,
|
||||||
)
|
)
|
||||||
fun getPrefHostUrl(preferences: SharedPreferences): String = preferences.getString(
|
fun getPrefHostUrl(preferences: SharedPreferences): String = preferences.getString(
|
||||||
HOSTURL_KEY, HOSTURL_DEFAULT
|
HOSTURL_KEY,
|
||||||
|
HOSTURL_DEFAULT,
|
||||||
)!!
|
)!!
|
||||||
fun getPrefUsername(preferences: SharedPreferences): String = preferences.getString(
|
fun getPrefUsername(preferences: SharedPreferences): String = preferences.getString(
|
||||||
USERNAME_KEY, ""
|
USERNAME_KEY,
|
||||||
|
"",
|
||||||
)!!
|
)!!
|
||||||
fun getPrefPassword(preferences: SharedPreferences): String = preferences.getString(
|
fun getPrefPassword(preferences: SharedPreferences): String = preferences.getString(
|
||||||
PASSWORD_KEY, ""
|
PASSWORD_KEY,
|
||||||
|
"",
|
||||||
)!!
|
)!!
|
||||||
fun getPrefParentId(preferences: SharedPreferences): String = preferences.getString(
|
fun getPrefParentId(preferences: SharedPreferences): String = preferences.getString(
|
||||||
MEDIALIB_KEY, ""
|
MEDIALIB_KEY,
|
||||||
|
"",
|
||||||
)!!
|
)!!
|
||||||
|
|
||||||
const val PREF_AUDIO_KEY = "preferred_audioLang"
|
const val PREF_AUDIO_KEY = "preferred_audioLang"
|
||||||
@ -54,7 +60,7 @@ object JFConstants {
|
|||||||
Quality(1920, 1080, 39808000, 192000, "1080p - 40 Mbps"),
|
Quality(1920, 1080, 39808000, 192000, "1080p - 40 Mbps"),
|
||||||
Quality(1920, 1080, 59808000, 192000, "1080p - 60 Mbps"),
|
Quality(1920, 1080, 59808000, 192000, "1080p - 60 Mbps"),
|
||||||
Quality(3840, 2160, 80000000, 192000, "4K - 80 Mbps"),
|
Quality(3840, 2160, 80000000, 192000, "4K - 80 Mbps"),
|
||||||
Quality(3840, 2160, 120000000, 192000, "4K - 120 Mbps")
|
Quality(3840, 2160, 120000000, 192000, "4K - 120 Mbps"),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Quality(
|
data class Quality(
|
||||||
@ -91,7 +97,7 @@ object JFConstants {
|
|||||||
"tem", "ter", "tet", "tgk", "tgl", "tha", "tig", "tir", "tiv", "tkl", "tlh", "tli", "tmh", "tog", "ton", "tpi", "tsi",
|
"tem", "ter", "tet", "tgk", "tgl", "tha", "tig", "tir", "tiv", "tkl", "tlh", "tli", "tmh", "tog", "ton", "tpi", "tsi",
|
||||||
"tsn", "tso", "tuk", "tum", "tup", "tur", "tvl", "twi", "tyv", "udm", "uga", "uig", "ukr", "umb", "urd", "uzb", "vai",
|
"tsn", "tso", "tuk", "tum", "tup", "tur", "tvl", "twi", "tyv", "udm", "uga", "uig", "ukr", "umb", "urd", "uzb", "vai",
|
||||||
"ven", "vie", "vol", "vot", "wal", "war", "was", "wen", "wln", "wol", "xal", "xho", "yao", "yap", "yid", "yor", "zap",
|
"ven", "vie", "vol", "vot", "wal", "war", "was", "wen", "wln", "wol", "xal", "xho", "yao", "yap", "yid", "yor", "zap",
|
||||||
"zbl", "zen", "zgh", "zha", "zho", "zul", "zun", "zza"
|
"zbl", "zen", "zgh", "zha", "zho", "zul", "zun", "zza",
|
||||||
)
|
)
|
||||||
|
|
||||||
val PREF_ENTRIES = arrayOf(
|
val PREF_ENTRIES = arrayOf(
|
||||||
@ -163,6 +169,6 @@ object JFConstants {
|
|||||||
"ꕙꔤ", "Tshivenḓa", "Tiếng Việt", "Volapük", "vađđa ceeli", "Wolaitta; Wolaytta", "Winaray; Samareño; Lineyte-Samarnon; Binisayâ nga Winaray; Binisayâ nga Samar-Leyte; “Binisayâ nga Waray”",
|
"ꕙꔤ", "Tshivenḓa", "Tiếng Việt", "Volapük", "vađđa ceeli", "Wolaitta; Wolaytta", "Winaray; Samareño; Lineyte-Samarnon; Binisayâ nga Winaray; Binisayâ nga Samar-Leyte; “Binisayâ nga Waray”",
|
||||||
"wá:šiw ʔítlu", "Serbsce / Serbski", "Walon", "Wolof", "Хальмг келн / Xaľmg keln", "isiXhosa", "Yao", "Yapese",
|
"wá:šiw ʔítlu", "Serbsce / Serbski", "Walon", "Wolof", "Хальмг келн / Xaľmg keln", "isiXhosa", "Yao", "Yapese",
|
||||||
"ייִדיש; יידיש; אידיש Yidiš", "èdè Yorùbá", "Diidxazá/Dizhsa", "Blissymbols; Blissymbolics; Bliss", "Tuḍḍungiyya",
|
"ייִדיש; יידיש; אידיש Yidiš", "èdè Yorùbá", "Diidxazá/Dizhsa", "Blissymbols; Blissymbolics; Bliss", "Tuḍḍungiyya",
|
||||||
"ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", "Vahcuengh / 話僮", "中文 Zhōngwén; 汉语; 漢語 Hànyǔ", "isiZulu", "Shiwi'ma", "kirmanckî; dimilkî; kirdkî; zazakî"
|
"ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", "Vahcuengh / 話僮", "中文 Zhōngwén; 汉语; 漢語 Hànyǔ", "isiZulu", "Shiwi'ma", "kirmanckî; dimilkî; kirdkî; zazakî",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -203,12 +203,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
url.addQueryParameter("SearchTerm", query)
|
url.addQueryParameter("SearchTerm", query)
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(url.build().toString(), headers = headers)
|
GET(url.build().toString(), headers = headers),
|
||||||
).execute()
|
).execute()
|
||||||
val items = json.decodeFromString<ItemsResponse>(response.body!!.string())
|
val items = json.decodeFromString<ItemsResponse>(response.body.string())
|
||||||
items.Items.forEach {
|
items.Items.forEach {
|
||||||
animeList.addAll(
|
animeList.addAll(
|
||||||
getAnimeFromId(it.Id)
|
getAnimeFromId(it.Id),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
url.addQueryParameter("ParentId", id)
|
url.addQueryParameter("ParentId", id)
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(url.build().toString())
|
GET(url.build().toString()),
|
||||||
).execute()
|
).execute()
|
||||||
return animeParse(response, 0).animes
|
return animeParse(response, 0).animes
|
||||||
}
|
}
|
||||||
@ -253,7 +253,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val info = json.decodeFromString<ItemsResponse.Item>(response.body!!.string())
|
val info = json.decodeFromString<ItemsResponse.Item>(response.body.string())
|
||||||
|
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
info.Overview
|
info.Overview
|
||||||
.replace("<br>\n", "br2n")
|
.replace("<br>\n", "br2n")
|
||||||
.replace("<br>", "br2n")
|
.replace("<br>", "br2n")
|
||||||
.replace("\n", "br2n")
|
.replace("\n", "br2n"),
|
||||||
).text().replace("br2n", "\n")
|
).text().replace("br2n", "\n")
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
@ -289,14 +289,14 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val episodeList = if (response.request.url.toString().startsWith("$baseUrl/Users/")) {
|
val episodeList = if (response.request.url.toString().startsWith("$baseUrl/Users/")) {
|
||||||
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body!!.string())
|
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body.string())
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.episode_number = 1.0F
|
episode.episode_number = 1.0F
|
||||||
episode.name = "Movie ${parsed.Name}"
|
episode.name = "Movie ${parsed.Name}"
|
||||||
episode.setUrlWithoutDomain(response.request.url.toString().substringAfter(baseUrl))
|
episode.setUrlWithoutDomain(response.request.url.toString().substringAfter(baseUrl))
|
||||||
listOf(episode)
|
listOf(episode)
|
||||||
} else {
|
} else {
|
||||||
val parsed = json.decodeFromString<ItemsResponse>(response.body!!.string())
|
val parsed = json.decodeFromString<ItemsResponse>(response.body.string())
|
||||||
|
|
||||||
parsed.Items.map { ep ->
|
parsed.Items.map { ep ->
|
||||||
|
|
||||||
@ -326,12 +326,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val id = json.decodeFromString<ItemsResponse.Item>(response.body!!.string()).Id
|
val id = json.decodeFromString<ItemsResponse.Item>(response.body.string()).Id
|
||||||
|
|
||||||
val sessionResponse = client.newCall(
|
val sessionResponse = client.newCall(
|
||||||
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey")
|
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey"),
|
||||||
).execute()
|
).execute()
|
||||||
val parsed = json.decodeFromString<SessionResponse>(sessionResponse.body!!.string())
|
val parsed = json.decodeFromString<SessionResponse>(sessionResponse.body.string())
|
||||||
|
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
|
|
||||||
@ -401,10 +401,12 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
url.addQueryParameter("VideoCodec", "h264")
|
url.addQueryParameter("VideoCodec", "h264")
|
||||||
url.addQueryParameter("VideoCodec", "h264")
|
url.addQueryParameter("VideoCodec", "h264")
|
||||||
url.addQueryParameter(
|
url.addQueryParameter(
|
||||||
"VideoBitrate", quality.videoBitrate.toString()
|
"VideoBitrate",
|
||||||
|
quality.videoBitrate.toString(),
|
||||||
)
|
)
|
||||||
url.addQueryParameter(
|
url.addQueryParameter(
|
||||||
"AudioBitrate", quality.audioBitrate.toString()
|
"AudioBitrate",
|
||||||
|
quality.audioBitrate.toString(),
|
||||||
)
|
)
|
||||||
url.addQueryParameter("PlaySessionId", parsed.PlaySessionId)
|
url.addQueryParameter("PlaySessionId", parsed.PlaySessionId)
|
||||||
url.addQueryParameter("TranscodingMaxAudioChannels", "6")
|
url.addQueryParameter("TranscodingMaxAudioChannels", "6")
|
||||||
@ -434,7 +436,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
private fun animeParse(response: Response, page: Int): AnimesPage {
|
private fun animeParse(response: Response, page: Int): AnimesPage {
|
||||||
val items = json.decodeFromString<ItemsResponse>(response.body!!.string())
|
val items = json.decodeFromString<ItemsResponse>(response.body.string())
|
||||||
val animesList = mutableListOf<SAnime>()
|
val animesList = mutableListOf<SAnime>()
|
||||||
|
|
||||||
items.Items.forEach { item ->
|
items.Items.forEach { item ->
|
||||||
@ -446,8 +448,8 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
LinkData(
|
LinkData(
|
||||||
path = "/Shows/${item.SeriesId}/Episodes?SeasonId=${item.Id}&api_key=$apiKey",
|
path = "/Shows/${item.SeriesId}/Episodes?SeasonId=${item.Id}&api_key=$apiKey",
|
||||||
seriesId = item.SeriesId!!,
|
seriesId = item.SeriesId!!,
|
||||||
seasonId = item.Id
|
seasonId = item.Id,
|
||||||
).toJsonString()
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
// Virtual if show doesn't have any sub-folders, i.e. no seasons
|
// Virtual if show doesn't have any sub-folders, i.e. no seasons
|
||||||
if (item.LocationType == "Virtual") {
|
if (item.LocationType == "Virtual") {
|
||||||
@ -471,8 +473,8 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
LinkData(
|
LinkData(
|
||||||
"/Users/$userId/Items/${item.Id}?api_key=$apiKey",
|
"/Users/$userId/Items/${item.Id}?api_key=$apiKey",
|
||||||
item.Id,
|
item.Id,
|
||||||
item.Id
|
item.Id,
|
||||||
).toJsonString()
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
animesList.add(anime)
|
animesList.add(anime)
|
||||||
}
|
}
|
||||||
@ -489,7 +491,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
url.addQueryParameter("EnableImageTypes", "Primary")
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(url.build().toString(), headers = headers)
|
GET(url.build().toString(), headers = headers),
|
||||||
).execute()
|
).execute()
|
||||||
animesList.addAll(animeParse(response, page).animes)
|
animesList.addAll(animeParse(response, page).animes)
|
||||||
}
|
}
|
||||||
@ -510,18 +512,36 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val mediaLibPref = medialibPreference(screen)
|
val mediaLibPref = medialibPreference(screen)
|
||||||
screen.addPreference(
|
screen.addPreference(
|
||||||
screen.editTextPreference(
|
screen.editTextPreference(
|
||||||
JFConstants.HOSTURL_KEY, JFConstants.HOSTURL_TITLE, JFConstants.HOSTURL_DEFAULT, baseUrl, false, "", mediaLibPref
|
JFConstants.HOSTURL_KEY,
|
||||||
)
|
JFConstants.HOSTURL_TITLE,
|
||||||
|
JFConstants.HOSTURL_DEFAULT,
|
||||||
|
baseUrl,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
mediaLibPref,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
screen.addPreference(
|
screen.addPreference(
|
||||||
screen.editTextPreference(
|
screen.editTextPreference(
|
||||||
JFConstants.USERNAME_KEY, JFConstants.USERNAME_TITLE, "", username, false, "", mediaLibPref
|
JFConstants.USERNAME_KEY,
|
||||||
)
|
JFConstants.USERNAME_TITLE,
|
||||||
|
"",
|
||||||
|
username,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
mediaLibPref,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
screen.addPreference(
|
screen.addPreference(
|
||||||
screen.editTextPreference(
|
screen.editTextPreference(
|
||||||
JFConstants.PASSWORD_KEY, JFConstants.PASSWORD_TITLE, "", password, true, "••••••••", mediaLibPref
|
JFConstants.PASSWORD_KEY,
|
||||||
)
|
JFConstants.PASSWORD_TITLE,
|
||||||
|
"",
|
||||||
|
password,
|
||||||
|
true,
|
||||||
|
"••••••••",
|
||||||
|
mediaLibPref,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
screen.addPreference(mediaLibPref)
|
screen.addPreference(mediaLibPref)
|
||||||
val subLangPref = ListPreference(screen.context).apply {
|
val subLangPref = ListPreference(screen.context).apply {
|
||||||
@ -587,9 +607,9 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
val mediaLibsResponse = client.newCall(
|
val mediaLibsResponse = client.newCall(
|
||||||
GET("$baseUrl/Users/$userId/Items?api_key=$apiKey")
|
GET("$baseUrl/Users/$userId/Items?api_key=$apiKey"),
|
||||||
).execute()
|
).execute()
|
||||||
val mediaJson = mediaLibsResponse.body?.let { json.decodeFromString<ItemsResponse>(it.string()) }?.Items
|
val mediaJson = mediaLibsResponse.body.let { json.decodeFromString<ItemsResponse>(it.string()) }?.Items
|
||||||
|
|
||||||
val entriesArray = mutableListOf<String>()
|
val entriesArray = mutableListOf<String>()
|
||||||
val entriesValueArray = mutableListOf<String>()
|
val entriesValueArray = mutableListOf<String>()
|
||||||
|
@ -49,7 +49,7 @@ class JellyfinAuthenticator(
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
.toRequestBody("application/json".toMediaType())
|
.toRequestBody("application/json".toMediaType())
|
||||||
val request = POST("$baseUrl/Users/authenticatebyname", headers = authHeader, body = body)
|
val request = POST("$baseUrl/Users/authenticatebyname", headers = authHeader, body = body)
|
||||||
val response = client.newCall(request).execute().body?.string()
|
val response = client.newCall(request).execute().body.string()
|
||||||
return response?.let { Json.decodeFromString<JsonObject>(it) }
|
return response?.let { Json.decodeFromString<JsonObject>(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +68,13 @@ class JellyfinAuthenticator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getPrefDeviceId(): String? = preferences.getString(
|
private fun getPrefDeviceId(): String? = preferences.getString(
|
||||||
DEVICEID_KEY, null
|
DEVICEID_KEY,
|
||||||
|
null,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun setPrefDeviceId(value: String) = preferences.edit().putString(
|
private fun setPrefDeviceId(value: String) = preferences.edit().putString(
|
||||||
DEVICEID_KEY, value
|
DEVICEID_KEY,
|
||||||
|
value,
|
||||||
).apply()
|
).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,4 @@ ext {
|
|||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly libs.bundles.coroutines
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -22,7 +22,7 @@ import java.net.Proxy
|
|||||||
class AccessTokenInterceptor(
|
class AccessTokenInterceptor(
|
||||||
private val crUrl: String,
|
private val crUrl: String,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
private val preferences: SharedPreferences
|
private val preferences: SharedPreferences,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
@ -42,7 +42,7 @@ class AccessTokenInterceptor(
|
|||||||
val refreshedToken = refreshAccessToken()
|
val refreshedToken = refreshAccessToken()
|
||||||
// Retry the request
|
// Retry the request
|
||||||
return chain.proceed(
|
return chain.proceed(
|
||||||
newRequestWithAccessToken(chain.request(), refreshedToken)
|
newRequestWithAccessToken(chain.request(), refreshedToken),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,8 +68,8 @@ class AccessTokenInterceptor(
|
|||||||
.proxy(
|
.proxy(
|
||||||
Proxy(
|
Proxy(
|
||||||
Proxy.Type.SOCKS,
|
Proxy.Type.SOCKS,
|
||||||
InetSocketAddress("cr-unblocker.us.to", 1080)
|
InetSocketAddress("cr-unblocker.us.to", 1080),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@ -78,29 +78,31 @@ class AccessTokenInterceptor(
|
|||||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||||
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Thanks Stormzy
|
// Thanks Stormzy
|
||||||
val refreshTokenResp = client.newCall(GET("https://raw.githubusercontent.com/Samfun75/File-host/main/aniyomi/refreshToken.txt")).execute()
|
val refreshTokenResp = client.newCall(GET("https://raw.githubusercontent.com/Samfun75/File-host/main/aniyomi/refreshToken.txt")).execute()
|
||||||
val refreshToken = refreshTokenResp.body!!.string().replace("[\n\r]".toRegex(), "")
|
val refreshToken = refreshTokenResp.body.string().replace("[\n\r]".toRegex(), "")
|
||||||
val headers = Headers.headersOf(
|
val headers = Headers.headersOf(
|
||||||
"Content-Type", "application/x-www-form-urlencoded",
|
"Content-Type",
|
||||||
"Authorization", "Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8="
|
"application/x-www-form-urlencoded",
|
||||||
|
"Authorization",
|
||||||
|
"Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8=",
|
||||||
)
|
)
|
||||||
val postBody = "grant_type=refresh_token&refresh_token=$refreshToken&scope=offline_access".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
val postBody = "grant_type=refresh_token&refresh_token=$refreshToken&scope=offline_access".toRequestBody("application/x-www-form-urlencoded".toMediaType())
|
||||||
val response = proxy.newCall(POST("$crUrl/auth/v1/token", headers, postBody)).execute()
|
val response = proxy.newCall(POST("$crUrl/auth/v1/token", headers, postBody)).execute()
|
||||||
val parsedJson = json.decodeFromString<AccessToken>(response.body!!.string())
|
val parsedJson = json.decodeFromString<AccessToken>(response.body.string())
|
||||||
|
|
||||||
val policy = proxy.newCall(newRequestWithAccessToken(GET("$crUrl/index/v2"), parsedJson)).execute()
|
val policy = proxy.newCall(newRequestWithAccessToken(GET("$crUrl/index/v2"), parsedJson)).execute()
|
||||||
val policyJson = json.decodeFromString<Policy>(policy.body!!.string())
|
val policyJson = json.decodeFromString<Policy>(policy.body.string())
|
||||||
val allTokens = AccessToken(
|
val allTokens = AccessToken(
|
||||||
parsedJson.access_token,
|
parsedJson.access_token,
|
||||||
parsedJson.token_type,
|
parsedJson.token_type,
|
||||||
policyJson.cms.policy,
|
policyJson.cms.policy,
|
||||||
policyJson.cms.signature,
|
policyJson.cms.signature,
|
||||||
policyJson.cms.key_pair_id,
|
policyJson.cms.key_pair_id,
|
||||||
policyJson.cms.bucket
|
policyJson.cms.bucket,
|
||||||
)
|
)
|
||||||
preferences.edit().putString(TOKEN_PREF_KEY, allTokens.toJsonString()).apply()
|
preferences.edit().putString(TOKEN_PREF_KEY, allTokens.toJsonString()).apply()
|
||||||
return allTokens
|
return allTokens
|
||||||
|
@ -11,38 +11,38 @@ data class AccessToken(
|
|||||||
val policy: String? = null,
|
val policy: String? = null,
|
||||||
val signature: String? = null,
|
val signature: String? = null,
|
||||||
val key_pair_id: String? = null,
|
val key_pair_id: String? = null,
|
||||||
val bucket: String? = null
|
val bucket: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Policy(
|
data class Policy(
|
||||||
val cms: Tokens
|
val cms: Tokens,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Tokens(
|
data class Tokens(
|
||||||
val policy: String,
|
val policy: String,
|
||||||
val signature: String,
|
val signature: String,
|
||||||
val key_pair_id: String,
|
val key_pair_id: String,
|
||||||
val bucket: String
|
val bucket: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LinkData(
|
data class LinkData(
|
||||||
val id: String,
|
val id: String,
|
||||||
val media_type: String
|
val media_type: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Images(
|
data class Images(
|
||||||
val poster_tall: List<ArrayList<Image>>? = null
|
val poster_tall: List<ArrayList<Image>>? = null,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Image(
|
data class Image(
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val type: String,
|
val type: String,
|
||||||
val source: String
|
val source: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ data class Anime(
|
|||||||
val series_metadata: Metadata? = null,
|
val series_metadata: Metadata? = null,
|
||||||
@SerialName("movie_listing_metadata")
|
@SerialName("movie_listing_metadata")
|
||||||
val movie_metadata: MovieMeta? = null,
|
val movie_metadata: MovieMeta? = null,
|
||||||
val content_provider: String? = null
|
val content_provider: String? = null,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Metadata(
|
data class Metadata(
|
||||||
@ -69,7 +69,7 @@ data class Anime(
|
|||||||
val is_dubbed: Boolean,
|
val is_dubbed: Boolean,
|
||||||
val is_subbed: Boolean,
|
val is_subbed: Boolean,
|
||||||
@SerialName("tenant_categories")
|
@SerialName("tenant_categories")
|
||||||
val genres: ArrayList<String>? = null
|
val genres: ArrayList<String>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -79,14 +79,14 @@ data class Anime(
|
|||||||
val maturity_ratings: ArrayList<String>,
|
val maturity_ratings: ArrayList<String>,
|
||||||
val subtitle_locales: ArrayList<String>,
|
val subtitle_locales: ArrayList<String>,
|
||||||
@SerialName("tenant_categories")
|
@SerialName("tenant_categories")
|
||||||
val genres: ArrayList<String>? = null
|
val genres: ArrayList<String>? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AnimeResult(
|
data class AnimeResult(
|
||||||
val total: Int,
|
val total: Int,
|
||||||
val data: ArrayList<Anime>
|
val data: ArrayList<Anime>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -97,28 +97,28 @@ data class SearchAnimeResult(
|
|||||||
data class SearchAnime(
|
data class SearchAnime(
|
||||||
val type: String,
|
val type: String,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
val items: ArrayList<Anime>
|
val items: ArrayList<Anime>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SeasonResult(
|
data class SeasonResult(
|
||||||
val total: Int,
|
val total: Int,
|
||||||
val data: ArrayList<Season>
|
val data: ArrayList<Season>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Season(
|
data class Season(
|
||||||
val id: String,
|
val id: String,
|
||||||
val season_number: Int? = null,
|
val season_number: Int? = null,
|
||||||
@SerialName("premium_available_date")
|
@SerialName("premium_available_date")
|
||||||
val date: String? = null
|
val date: String? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeResult(
|
data class EpisodeResult(
|
||||||
val total: Int,
|
val total: Int,
|
||||||
val data: ArrayList<Episode>
|
val data: ArrayList<Episode>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Episode(
|
data class Episode(
|
||||||
@ -130,13 +130,13 @@ data class EpisodeResult(
|
|||||||
@SerialName("episode_air_date")
|
@SerialName("episode_air_date")
|
||||||
val airDate: String? = null,
|
val airDate: String? = null,
|
||||||
val versions: ArrayList<Version>? = null,
|
val versions: ArrayList<Version>? = null,
|
||||||
val streams_link: String
|
val streams_link: String,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Version(
|
data class Version(
|
||||||
val audio_locale: String,
|
val audio_locale: String,
|
||||||
@SerialName("media_guid")
|
@SerialName("media_guid")
|
||||||
val mediaId: String
|
val mediaId: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,19 +146,19 @@ data class TempEpisode(
|
|||||||
var name: String,
|
var name: String,
|
||||||
var episode_number: Float,
|
var episode_number: Float,
|
||||||
var date_upload: Long,
|
var date_upload: Long,
|
||||||
var scanlator: String?
|
var scanlator: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeData(
|
data class EpisodeData(
|
||||||
val ids: List<Pair<String, String>>
|
val ids: List<Pair<String, String>>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class VideoStreams(
|
data class VideoStreams(
|
||||||
val streams: Stream,
|
val streams: Stream,
|
||||||
val subtitles: JsonObject,
|
val subtitles: JsonObject,
|
||||||
val audio_locale: String
|
val audio_locale: String,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Stream(
|
data class Stream(
|
||||||
@ -169,13 +169,13 @@ data class VideoStreams(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class HlsLinks(
|
data class HlsLinks(
|
||||||
val hardsub_locale: String,
|
val hardsub_locale: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Subtitle(
|
data class Subtitle(
|
||||||
val locale: String,
|
val locale: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun <T> List<T>.thirdLast(): T? {
|
fun <T> List<T>.thirdLast(): T? {
|
||||||
|
@ -74,7 +74,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
val parsed = json.decodeFromString<AnimeResult>(response.body!!.string())
|
val parsed = json.decodeFromString<AnimeResult>(response.body.string())
|
||||||
val animeList = parsed.data.parallelMap { ani ->
|
val animeList = parsed.data.parallelMap { ani ->
|
||||||
runCatching {
|
runCatching {
|
||||||
ani.toSAnime()
|
ani.toSAnime()
|
||||||
@ -111,7 +111,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
val bod = response.body!!.string()
|
val bod = response.body.string()
|
||||||
val total: Int
|
val total: Int
|
||||||
val animeList = (
|
val animeList = (
|
||||||
if (response.request.url.encodedPath.contains("search")) {
|
if (response.request.url.encodedPath.contains("search")) {
|
||||||
@ -142,10 +142,13 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
||||||
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
val mediaId = json.decodeFromString<LinkData>(anime.url)
|
||||||
val resp = client.newCall(
|
val resp = client.newCall(
|
||||||
if (mediaId.media_type == "series") GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")
|
if (mediaId.media_type == "series") {
|
||||||
else GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}?locale=en-US")
|
GET("$crUrl/content/v2/cms/series/${mediaId.id}?locale=en-US")
|
||||||
|
} else {
|
||||||
|
GET("$crUrl/content/v2/cms/movie_listings/${mediaId.id}?locale=en-US")
|
||||||
|
},
|
||||||
).execute()
|
).execute()
|
||||||
val info = json.decodeFromString<AnimeResult>(resp.body!!.string())
|
val info = json.decodeFromString<AnimeResult>(resp.body.string())
|
||||||
return Observable.just(
|
return Observable.just(
|
||||||
anime.apply {
|
anime.apply {
|
||||||
author = info.data.first().content_provider
|
author = info.data.first().content_provider
|
||||||
@ -153,7 +156,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (genre.isNullOrBlank()) {
|
if (genre.isNullOrBlank()) {
|
||||||
genre = info.data.first().genres?.joinToString { gen -> gen.replaceFirstChar { it.uppercase() } }
|
genre = info.data.first().genres?.joinToString { gen -> gen.replaceFirstChar { it.uppercase() } }
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +174,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val seasons = json.decodeFromString<SeasonResult>(response.body!!.string())
|
val seasons = json.decodeFromString<SeasonResult>(response.body.string())
|
||||||
val series = response.request.url.encodedPath.contains("series/")
|
val series = response.request.url.encodedPath.contains("series/")
|
||||||
// Why all this? well crunchy sends same season twice with different quality eg. One Piece
|
// Why all this? well crunchy sends same season twice with different quality eg. One Piece
|
||||||
// which causes the number of episodes to be higher that what it actually is.
|
// which causes the number of episodes to be higher that what it actually is.
|
||||||
@ -184,7 +187,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes"))
|
client.newCall(GET("$crUrl/content/v2/cms/seasons/${seasonData.id}/episodes"))
|
||||||
.execute()
|
.execute()
|
||||||
val episodes =
|
val episodes =
|
||||||
json.decodeFromString<EpisodeResult>(episodeResp.body!!.string())
|
json.decodeFromString<EpisodeResult>(episodeResp.body.string())
|
||||||
episodes.data.sortedBy { it.episode_number }.parallelMap { ep ->
|
episodes.data.sortedBy { it.episode_number }.parallelMap { ep ->
|
||||||
TempEpisode(
|
TempEpisode(
|
||||||
epData = EpisodeData(
|
epData = EpisodeData(
|
||||||
@ -193,9 +196,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Pair(
|
Pair(
|
||||||
ep.streams_link.substringAfter("videos/")
|
ep.streams_link.substringAfter("videos/")
|
||||||
.substringBefore("/streams"),
|
.substringBefore("/streams"),
|
||||||
ep.audio_locale
|
ep.audio_locale,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
name = if (ep.episode_number > 0 && ep.episode.isNumeric()) {
|
name = if (ep.episode_number > 0 && ep.episode.isNumeric()) {
|
||||||
"Season ${seasonData.season_number} Ep ${df.format(ep.episode_number)}: " + ep.title
|
"Season ${seasonData.season_number} Ep ${df.format(ep.episode_number)}: " + ep.title
|
||||||
@ -206,7 +209,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
date_upload = ep.airDate?.let { parseDate(it) } ?: 0L,
|
date_upload = ep.airDate?.let { parseDate(it) } ?: 0L,
|
||||||
scanlator = ep.versions?.sortedBy { it.audio_locale }
|
scanlator = ep.versions?.sortedBy { it.audio_locale }
|
||||||
?.joinToString { it.audio_locale.substringBefore("-") }
|
?.joinToString { it.audio_locale.substringBefore("-") }
|
||||||
?: ep.audio_locale.substringBefore("-")
|
?: ep.audio_locale.substringBefore("-"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
@ -257,7 +260,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
private fun extractVideo(media: Pair<String, String>, policyJson: AccessToken): List<Video> {
|
private fun extractVideo(media: Pair<String, String>, policyJson: AccessToken): List<Video> {
|
||||||
val (mediaId, aud) = media
|
val (mediaId, aud) = media
|
||||||
val response = client.newCall(GET("$crUrl/cms/v2${policyJson.bucket}/videos/$mediaId/streams?Policy=${policyJson.policy}&Signature=${policyJson.signature}&Key-Pair-Id=${policyJson.key_pair_id}")).execute()
|
val response = client.newCall(GET("$crUrl/cms/v2${policyJson.bucket}/videos/$mediaId/streams?Policy=${policyJson.policy}&Signature=${policyJson.signature}&Key-Pair-Id=${policyJson.key_pair_id}")).execute()
|
||||||
val streams = json.decodeFromString<VideoStreams>(response.body!!.string())
|
val streams = json.decodeFromString<VideoStreams>(response.body.string())
|
||||||
|
|
||||||
var subsList = emptyList<Track>()
|
var subsList = emptyList<Track>()
|
||||||
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
|
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
|
||||||
@ -268,8 +271,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}.sortedWith(
|
}.sortedWith(
|
||||||
compareBy(
|
compareBy(
|
||||||
{ it.lang },
|
{ it.lang },
|
||||||
{ it.lang.contains(subLocale) }
|
{ it.lang.contains(subLocale) },
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
} catch (_: Error) {}
|
} catch (_: Error) {}
|
||||||
|
|
||||||
@ -277,7 +280,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return streams.streams.adaptive_hls.entries.parallelMap { (_, value) ->
|
return streams.streams.adaptive_hls.entries.parallelMap { (_, value) ->
|
||||||
val stream = json.decodeFromString<HlsLinks>(value.jsonObject.toString())
|
val stream = json.decodeFromString<HlsLinks>(value.jsonObject.toString())
|
||||||
runCatching {
|
runCatching {
|
||||||
val playlist = client.newCall(GET(stream.url)).execute().body!!.string()
|
val playlist = client.newCall(GET(stream.url)).execute().body.string()
|
||||||
playlist.substringAfter("#EXT-X-STREAM-INF:")
|
playlist.substringAfter("#EXT-X-STREAM-INF:")
|
||||||
.split("#EXT-X-STREAM-INF:").map {
|
.split("#EXT-X-STREAM-INF:").map {
|
||||||
val hardsub = stream.hardsub_locale.let { hs ->
|
val hardsub = stream.hardsub_locale.let { hs ->
|
||||||
@ -294,7 +297,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
videoUrl,
|
videoUrl,
|
||||||
quality,
|
quality,
|
||||||
videoUrl,
|
videoUrl,
|
||||||
subtitleTracks = if (hardsub.isNotBlank()) emptyList() else subsList
|
subtitleTracks = if (hardsub.isNotBlank()) emptyList() else subsList,
|
||||||
)
|
)
|
||||||
} catch (_: Error) {
|
} catch (_: Error) {
|
||||||
Video(videoUrl, quality, videoUrl)
|
Video(videoUrl, quality, videoUrl)
|
||||||
@ -341,7 +344,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Pair("ro-RO", "Romanian"),
|
Pair("ro-RO", "Romanian"),
|
||||||
Pair("sv-SE", "Swedish"),
|
Pair("sv-SE", "Swedish"),
|
||||||
Pair("zh-CN", "Chinese (PRC)"),
|
Pair("zh-CN", "Chinese (PRC)"),
|
||||||
Pair("zh-HK", "Chinese (Hong Kong)")
|
Pair("zh-HK", "Chinese (Hong Kong)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun LinkData.toJsonString(): String {
|
private fun LinkData.toJsonString(): String {
|
||||||
@ -372,12 +375,20 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (this@toSAnime.series_metadata?.subtitle_locales?.any() == true ||
|
if (this@toSAnime.series_metadata?.subtitle_locales?.any() == true ||
|
||||||
this@toSAnime.movie_metadata?.subtitle_locales?.any() == true ||
|
this@toSAnime.movie_metadata?.subtitle_locales?.any() == true ||
|
||||||
this@toSAnime.series_metadata?.is_subbed == true
|
this@toSAnime.series_metadata?.is_subbed == true
|
||||||
) " Sub" else ""
|
) {
|
||||||
|
" Sub"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
) +
|
) +
|
||||||
(
|
(
|
||||||
if (this@toSAnime.series_metadata?.audio_locales?.any() == true ||
|
if (this@toSAnime.series_metadata?.audio_locales?.any() == true ||
|
||||||
this@toSAnime.movie_metadata?.is_dubbed == true
|
this@toSAnime.movie_metadata?.is_dubbed == true
|
||||||
) " Dub" else ""
|
) {
|
||||||
|
" Dub"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
)
|
)
|
||||||
desc += "\nMaturity Ratings: " +
|
desc += "\nMaturity Ratings: " +
|
||||||
(
|
(
|
||||||
@ -410,8 +421,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
{ it.quality.contains(quality) },
|
{ it.quality.contains(quality) },
|
||||||
{ it.quality.contains("Aud: ${dubLocale.getLocale()}") },
|
{ it.quality.contains("Aud: ${dubLocale.getLocale()}") },
|
||||||
{ it.quality.contains("HardSub") == shouldContainHard },
|
{ it.quality.contains("HardSub") == shouldContainHard },
|
||||||
{ it.quality.contains(subLocale) }
|
{ it.quality.contains(subLocale) },
|
||||||
)
|
),
|
||||||
).reversed()
|
).reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ object YomirollFilters {
|
|||||||
|
|
||||||
open class QueryPartFilter(
|
open class QueryPartFilter(
|
||||||
displayName: String,
|
displayName: String,
|
||||||
val vals: Array<Pair<String, String>>
|
val vals: Array<Pair<String, String>>,
|
||||||
) : AnimeFilter.Select<String>(
|
) : AnimeFilter.Select<String>(
|
||||||
displayName,
|
displayName,
|
||||||
vals.map { it.first }.toTypedArray()
|
vals.map { it.first }.toTypedArray(),
|
||||||
) {
|
) {
|
||||||
fun toQueryPart() = vals[state].second
|
fun toQueryPart() = vals[state].second
|
||||||
}
|
}
|
||||||
@ -28,13 +28,15 @@ object YomirollFilters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified R> AnimeFilterList.parseCheckbox(
|
private inline fun <reified R> AnimeFilterList.parseCheckbox(
|
||||||
options: Array<Pair<String, String>>
|
options: Array<Pair<String, String>>,
|
||||||
): String {
|
): String {
|
||||||
return (this.getFirst<R>() as CheckBoxFilterList).state
|
return (this.getFirst<R>() as CheckBoxFilterList).state
|
||||||
.mapNotNull { checkbox ->
|
.mapNotNull { checkbox ->
|
||||||
if (checkbox.state)
|
if (checkbox.state) {
|
||||||
options.find { it.first == checkbox.name }!!.second
|
options.find { it.first == checkbox.name }!!.second
|
||||||
else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}.joinToString("")
|
}.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ object YomirollFilters {
|
|||||||
|
|
||||||
class LanguageFilter : CheckBoxFilterList(
|
class LanguageFilter : CheckBoxFilterList(
|
||||||
"Language",
|
"Language",
|
||||||
CrunchyFiltersData.language.map { CheckBoxVal(it.first, false) }
|
CrunchyFiltersData.language.map { CheckBoxVal(it.first, false) },
|
||||||
)
|
)
|
||||||
|
|
||||||
val filterList = AnimeFilterList(
|
val filterList = AnimeFilterList(
|
||||||
@ -56,7 +58,7 @@ object YomirollFilters {
|
|||||||
CategoryFilter(),
|
CategoryFilter(),
|
||||||
SortFilter(),
|
SortFilter(),
|
||||||
MediaFilter(),
|
MediaFilter(),
|
||||||
LanguageFilter()
|
LanguageFilter(),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class FilterSearchParams(
|
data class FilterSearchParams(
|
||||||
@ -64,7 +66,7 @@ object YomirollFilters {
|
|||||||
val category: String = "",
|
val category: String = "",
|
||||||
val sort: String = "",
|
val sort: String = "",
|
||||||
val language: String = "",
|
val language: String = "",
|
||||||
val media: String = ""
|
val media: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
||||||
@ -83,7 +85,7 @@ object YomirollFilters {
|
|||||||
val searchType = arrayOf(
|
val searchType = arrayOf(
|
||||||
Pair("Top Results", "top_results"),
|
Pair("Top Results", "top_results"),
|
||||||
Pair("Series", "series"),
|
Pair("Series", "series"),
|
||||||
Pair("Movies", "movie_listing")
|
Pair("Movies", "movie_listing"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val categories = arrayOf(
|
val categories = arrayOf(
|
||||||
@ -176,24 +178,24 @@ object YomirollFilters {
|
|||||||
Pair("Thriller, Drama", "&categories=thriller,drama"),
|
Pair("Thriller, Drama", "&categories=thriller,drama"),
|
||||||
Pair("Thriller, Fantasy", "&categories=thriller,fantasy"),
|
Pair("Thriller, Fantasy", "&categories=thriller,fantasy"),
|
||||||
Pair("Thriller, Sci-Fi", "&categories=thriller,sci-fi"),
|
Pair("Thriller, Sci-Fi", "&categories=thriller,sci-fi"),
|
||||||
Pair("Thriller, Supernatural", "&categories=thriller,supernatural")
|
Pair("Thriller, Supernatural", "&categories=thriller,supernatural"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val sortType = arrayOf(
|
val sortType = arrayOf(
|
||||||
Pair("Popular", "popularity"),
|
Pair("Popular", "popularity"),
|
||||||
Pair("New", "newly_added"),
|
Pair("New", "newly_added"),
|
||||||
Pair("Alphabetical", "alphabetical")
|
Pair("Alphabetical", "alphabetical"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val language = arrayOf(
|
val language = arrayOf(
|
||||||
Pair("Sub", "&is_subbed=true"),
|
Pair("Sub", "&is_subbed=true"),
|
||||||
Pair("Dub", "&is_dubbed=true")
|
Pair("Dub", "&is_dubbed=true"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val mediaType = arrayOf(
|
val mediaType = arrayOf(
|
||||||
Pair("All", ""),
|
Pair("All", ""),
|
||||||
Pair("Series", "&type=series"),
|
Pair("Series", "&type=series"),
|
||||||
Pair("Movies", "&type=movie_listing")
|
Pair("Movies", "&type=movie_listing"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class CategoryResponse(
|
data class CategoryResponse(
|
||||||
val data: List<CategoryData>
|
val data: List<CategoryData>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class CategoryData(
|
data class CategoryData(
|
||||||
@ -12,13 +12,13 @@ data class CategoryResponse(
|
|||||||
val domainType: Int,
|
val domainType: Int,
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val sort: String
|
val sort: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AnimeInfoResponse(
|
data class AnimeInfoResponse(
|
||||||
val data: InfoData
|
val data: InfoData,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class InfoData(
|
data class InfoData(
|
||||||
@ -33,45 +33,45 @@ data class AnimeInfoResponse(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeInfo(
|
data class EpisodeInfo(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val seriesNo: Float
|
val seriesNo: Float,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class IdInfo(
|
data class IdInfo(
|
||||||
val name: String
|
val name: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SearchResponse(
|
data class SearchResponse(
|
||||||
val data: InfoData
|
val data: InfoData,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class InfoData(
|
data class InfoData(
|
||||||
val results: List<CategoryResponse.CategoryData>
|
val results: List<CategoryResponse.CategoryData>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeResponse(
|
data class EpisodeResponse(
|
||||||
val data: EpisodeData
|
val data: EpisodeData,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeData(
|
data class EpisodeData(
|
||||||
val qualities: List<Quality>,
|
val qualities: List<Quality>,
|
||||||
val subtitles: List<Subtitle>
|
val subtitles: List<Subtitle>,
|
||||||
) {
|
) {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Quality(
|
data class Quality(
|
||||||
val quality: Int,
|
val quality: Int,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Subtitle(
|
data class Subtitle(
|
||||||
val language: String,
|
val language: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,5 +80,5 @@ data class EpisodeResponse(
|
|||||||
data class LinkData(
|
data class LinkData(
|
||||||
val category: String,
|
val category: String,
|
||||||
val id: String,
|
val id: String,
|
||||||
val episodeId: String? = null
|
val episodeId: String? = null,
|
||||||
)
|
)
|
||||||
|
@ -55,7 +55,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
val parsed = json.decodeFromString<CategoryResponse>(response.body!!.string())
|
val parsed = json.decodeFromString<CategoryResponse>(response.body.string())
|
||||||
if (parsed.data.isEmpty()) {
|
if (parsed.data.isEmpty()) {
|
||||||
return AnimesPage(emptyList(), false)
|
return AnimesPage(emptyList(), false)
|
||||||
}
|
}
|
||||||
@ -67,8 +67,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
setUrlWithoutDomain(
|
setUrlWithoutDomain(
|
||||||
LinkData(
|
LinkData(
|
||||||
ani.domainType.toString(),
|
ani.domainType.toString(),
|
||||||
ani.id
|
ani.id,
|
||||||
).toJsonString()
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return if (url.startsWith("/api/category")) {
|
return if (url.startsWith("/api/category")) {
|
||||||
popularAnimeParse(response)
|
popularAnimeParse(response)
|
||||||
} else {
|
} else {
|
||||||
val parsed = json.decodeFromString<SearchResponse>(response.body!!.string())
|
val parsed = json.decodeFromString<SearchResponse>(response.body.string())
|
||||||
if (parsed.data.results.isEmpty()) {
|
if (parsed.data.results.isEmpty()) {
|
||||||
return AnimesPage(emptyList(), false)
|
return AnimesPage(emptyList(), false)
|
||||||
}
|
}
|
||||||
@ -116,8 +116,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
setUrlWithoutDomain(
|
setUrlWithoutDomain(
|
||||||
LinkData(
|
LinkData(
|
||||||
ani.domainType.toString(),
|
ani.domainType.toString(),
|
||||||
ani.id
|
ani.id,
|
||||||
).toJsonString()
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Pair("Recent TV Series", "/category?area=&category=1&order=up¶ms=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
|
Pair("Recent TV Series", "/category?area=&category=1&order=up¶ms=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
|
||||||
Pair("Popular Anime", "/category?area=&category=1&order=count¶ms=COMIC&size=30"),
|
Pair("Popular Anime", "/category?area=&category=1&order=count¶ms=COMIC&size=30"),
|
||||||
Pair("Recent Anime", "/category?area=&category=1&order=up¶ms=COMIC&size=30"),
|
Pair("Recent Anime", "/category?area=&category=1&order=up¶ms=COMIC&size=30"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||||
@ -157,14 +157,14 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
|
||||||
val parsed = json.decodeFromString<LinkData>(anime.url)
|
val parsed = json.decodeFromString<LinkData>(anime.url)
|
||||||
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
|
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
|
||||||
val data = json.decodeFromString<AnimeInfoResponse>(resp.body!!.string()).data
|
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
|
||||||
return Observable.just(
|
return Observable.just(
|
||||||
anime.apply {
|
anime.apply {
|
||||||
title = data.name
|
title = data.name
|
||||||
thumbnail_url = data.coverVerticalUrl
|
thumbnail_url = data.coverVerticalUrl
|
||||||
description = data.introduction
|
description = data.introduction
|
||||||
genre = data.tagList.joinToString(", ") { it.name }
|
genre = data.tagList.joinToString(", ") { it.name }
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
||||||
val parsed = json.decodeFromString<LinkData>(anime.url)
|
val parsed = json.decodeFromString<LinkData>(anime.url)
|
||||||
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
|
val resp = client.newCall(GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}")).execute()
|
||||||
val data = json.decodeFromString<AnimeInfoResponse>(resp.body!!.string()).data
|
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
|
||||||
val episodeList = data.episodeVo.map { ep ->
|
val episodeList = data.episodeVo.map { ep ->
|
||||||
val formattedEpNum = if (floor(ep.seriesNo) == ceil(ep.seriesNo)) {
|
val formattedEpNum = if (floor(ep.seriesNo) == ceil(ep.seriesNo)) {
|
||||||
ep.seriesNo.toInt()
|
ep.seriesNo.toInt()
|
||||||
@ -188,8 +188,8 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
LinkData(
|
LinkData(
|
||||||
data.category.toString(),
|
data.category.toString(),
|
||||||
data.id,
|
data.id,
|
||||||
ep.id.toString()
|
ep.id.toString(),
|
||||||
).toJsonString()
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
name = "Episode $formattedEpNum"
|
name = "Episode $formattedEpNum"
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
||||||
val parsed = json.decodeFromString<LinkData>(episode.url)
|
val parsed = json.decodeFromString<LinkData>(episode.url)
|
||||||
val resp = client.newCall(GET("$baseUrl/episode?category=${parsed.category}&id=${parsed.id}&episode=${parsed.episodeId!!}")).execute()
|
val resp = client.newCall(GET("$baseUrl/episode?category=${parsed.category}&id=${parsed.id}&episode=${parsed.episodeId!!}")).execute()
|
||||||
val episodeParsed = json.decodeFromString<EpisodeResponse>(resp.body!!.string())
|
val episodeParsed = json.decodeFromString<EpisodeResponse>(resp.body.string())
|
||||||
val subtitleList = episodeParsed.data.subtitles.map { sub ->
|
val subtitleList = episodeParsed.data.subtitles.map { sub ->
|
||||||
Track(sub.url, sub.language)
|
Track(sub.url, sub.language)
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val quality = preferences.getString("preferred_quality", "1080")!!
|
val quality = preferences.getString("preferred_quality", "1080")!!
|
||||||
|
|
||||||
return this.sortedWith(
|
return this.sortedWith(
|
||||||
compareBy { it.quality.contains(quality) }
|
compareBy { it.quality.contains(quality) },
|
||||||
).reversed()
|
).reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,4 @@ ext {
|
|||||||
containsNsfw = false
|
containsNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly libs.bundles.coroutines
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -72,7 +72,7 @@ open class Onepace(override val lang: String, override val name: String) : Confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ class OnepaceFactory : AnimeSourceFactory {
|
|||||||
override fun createSources(): List<AnimeSource> = listOf(
|
override fun createSources(): List<AnimeSource> = listOf(
|
||||||
OnepaceEspa(),
|
OnepaceEspa(),
|
||||||
OnepaceFr(),
|
OnepaceFr(),
|
||||||
OnepaceEn()
|
OnepaceEn(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import org.jsoup.Jsoup
|
|||||||
class ZippyExtractor {
|
class ZippyExtractor {
|
||||||
fun getVideoUrl(url: String, json: Json): String {
|
fun getVideoUrl(url: String, json: Json): String {
|
||||||
val document = Jsoup.connect(url).get()
|
val document = Jsoup.connect(url).get()
|
||||||
val jscript = document.selectFirst("script:containsData(dlbutton)").data()
|
val jscript = document.selectFirst("script:containsData(dlbutton)")!!.data()
|
||||||
.replace("document.getElementById('dlbutton').href", "a")
|
.replace("document.getElementById('dlbutton').href", "a")
|
||||||
.replace("document.getElementById('fimage').href", "b")
|
.replace("document.getElementById('fimage').href", "b")
|
||||||
.replace("document.getElementById('fimage')", "false")
|
.replace("document.getElementById('fimage')", "false")
|
||||||
|
@ -100,7 +100,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val iframe = "https://akwam.im/watch" + document.select("a.link-show").attr("href").substringAfter("watch") + "/" + document.ownerDocument().select("input#page_id").attr("value")
|
val iframe = "https://akwam.im/watch" + document.select("a.link-show").attr("href").substringAfter("watch") + "/" + document.ownerDocument()!!.select("input#page_id").attr("value")
|
||||||
val referer = response.request.url.toString()
|
val referer = response.request.url.toString()
|
||||||
val refererHeaders = Headers.headersOf("referer", referer)
|
val refererHeaders = Headers.headersOf("referer", referer)
|
||||||
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
|
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
|
||||||
@ -229,13 +229,13 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private class RatingSFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التقييم", vals)
|
private class RatingSFilter(vals: Array<Pair<String?, String>>) : UriPartFilter("التقييم", vals)
|
||||||
private fun getTypeFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getTypeFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
Pair("movies", "افلام"),
|
Pair("movies", "افلام"),
|
||||||
Pair("series", "مسلسلات")
|
Pair("series", "مسلسلات"),
|
||||||
)
|
)
|
||||||
private fun getSectionFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getSectionFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
Pair("0", "الكل"),
|
Pair("0", "الكل"),
|
||||||
Pair("movie", "افلام"),
|
Pair("movie", "افلام"),
|
||||||
Pair("series", "مسلسلات"),
|
Pair("series", "مسلسلات"),
|
||||||
Pair("show", "تلفزيون")
|
Pair("show", "تلفزيون"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getRatingFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getRatingFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -248,7 +248,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("6", "6+"),
|
Pair("6", "6+"),
|
||||||
Pair("7", "7+"),
|
Pair("7", "7+"),
|
||||||
Pair("8", "8+"),
|
Pair("8", "8+"),
|
||||||
Pair("9", "9+")
|
Pair("9", "9+"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getFormatFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getFormatFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -266,7 +266,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("HDTC", "HDTC"),
|
Pair("HDTC", "HDTC"),
|
||||||
Pair("BDRIP", "BDRIP"),
|
Pair("BDRIP", "BDRIP"),
|
||||||
Pair("HDRIP", "HDRIP"),
|
Pair("HDRIP", "HDRIP"),
|
||||||
Pair("HC+HDRIP", "HC HDRIP")
|
Pair("HC+HDRIP", "HC HDRIP"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getQualityFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getQualityFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -286,7 +286,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("30", "اجنبي"),
|
Pair("30", "اجنبي"),
|
||||||
Pair("31", "هندي"),
|
Pair("31", "هندي"),
|
||||||
Pair("32", "تركي"),
|
Pair("32", "تركي"),
|
||||||
Pair("33", "اسيوي")
|
Pair("33", "اسيوي"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getCategorySFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getCategorySFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -315,7 +315,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("22", "رعب"),
|
Pair("22", "رعب"),
|
||||||
Pair("21", "جريمة"),
|
Pair("21", "جريمة"),
|
||||||
Pair("19", "مغامرة"),
|
Pair("19", "مغامرة"),
|
||||||
Pair("91", "غربي")
|
Pair("91", "غربي"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getRatingSFilter(): Array<Pair<String?, String>> = arrayOf(
|
private fun getRatingSFilter(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -328,7 +328,7 @@ class Akwam : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("6", "6+"),
|
Pair("6", "6+"),
|
||||||
Pair("7", "7+"),
|
Pair("7", "7+"),
|
||||||
Pair("8", "8+"),
|
Pair("8", "8+"),
|
||||||
Pair("9", "9+")
|
Pair("9", "9+"),
|
||||||
)
|
)
|
||||||
|
|
||||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
|
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
|
||||||
|
@ -107,7 +107,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
||||||
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
||||||
.execute().asJsoup()
|
.execute().asJsoup()
|
||||||
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
|
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
|
||||||
} else {
|
} else {
|
||||||
val postUrl = document.select("form[method=post]").attr("action")
|
val postUrl = document.select("form[method=post]").attr("action")
|
||||||
val ur = document.select("input[name=ur]").attr("value")
|
val ur = document.select("input[name=ur]").attr("value")
|
||||||
@ -141,7 +141,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val elements = element.select(videoListSelector())
|
val elements = element.select(videoListSelector())
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val location = element.ownerDocument().location()
|
val location = element.ownerDocument()!!.location()
|
||||||
val embedUrl = element.attr("data-ep-url")
|
val embedUrl = element.attr("data-ep-url")
|
||||||
val qualityy = element.text()
|
val qualityy = element.text()
|
||||||
Log.i("embedUrl", "$embedUrl")
|
Log.i("embedUrl", "$embedUrl")
|
||||||
@ -297,6 +297,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return GET(typeUrl.toString(), headers)
|
return GET(typeUrl.toString(), headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw Exception("اختر فلتر")
|
throw Exception("اختر فلتر")
|
||||||
@ -308,7 +309,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.thumbnail_url = document.select("img.thumbnail").first().attr("src")
|
anime.thumbnail_url = document.selectFirst("img.thumbnail")!!.attr("src")
|
||||||
anime.title = document.select("h1.anime-details-title").text()
|
anime.title = document.select("h1.anime-details-title").text()
|
||||||
anime.genre = document.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
|
anime.genre = document.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
|
||||||
anime.description = document.select("p.anime-story").text()
|
anime.description = document.select("p.anime-story").text()
|
||||||
@ -421,7 +422,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Genre("مغامرات", "%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa/"),
|
Genre("مغامرات", "%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa/"),
|
||||||
Genre("موسيقي", "%d9%85%d9%88%d8%b3%d9%8a%d9%82%d9%8a/"),
|
Genre("موسيقي", "%d9%85%d9%88%d8%b3%d9%8a%d9%82%d9%8a/"),
|
||||||
Genre("ميكا", "%d9%85%d9%8a%d9%83%d8%a7/"),
|
Genre("ميكا", "%d9%85%d9%8a%d9%83%d8%a7/"),
|
||||||
Genre("نفسي", "%d9%86%d9%81%d8%b3%d9%8a/")
|
Genre("نفسي", "%d9%86%d9%81%d8%b3%d9%8a/"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getTypeList() = listOf(
|
private fun getTypeList() = listOf(
|
||||||
@ -430,7 +431,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Type("ONA", "ona1"),
|
Type("ONA", "ona1"),
|
||||||
Type("OVA", "ova1"),
|
Type("OVA", "ova1"),
|
||||||
Type("Special", "special1"),
|
Type("Special", "special1"),
|
||||||
Type("TV", "tv2")
|
Type("TV", "tv2"),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -438,7 +439,7 @@ class Anime4Up : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Status("أختر", ""),
|
Status("أختر", ""),
|
||||||
Status("لم يعرض بعد", "%d9%84%d9%85-%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a8%d8%b9%d8%af"),
|
Status("لم يعرض بعد", "%d9%84%d9%85-%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a8%d8%b9%d8%af"),
|
||||||
Status("مكتمل", "complete"),
|
Status("مكتمل", "complete"),
|
||||||
Status("يعرض الان", "%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a7%d9%84%d8%a7%d9%86-1")
|
Status("يعرض الان", "%d9%8a%d8%b9%d8%b1%d8%b6-%d8%a7%d9%84%d8%a7%d9%86-1"),
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
||||||
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
||||||
val sojson = REGEX_SOJSON.getFirst(eval)
|
val sojson = REGEX_SOJSON.getFirst(eval)
|
||||||
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
hashAlgorithm: String = "MD5",
|
hashAlgorithm: String = "MD5",
|
||||||
keyLength: Int = 32,
|
keyLength: Int = 32,
|
||||||
ivLength: Int = 16,
|
ivLength: Int = 16,
|
||||||
iterations: Int = 1
|
iterations: Int = 1,
|
||||||
): List<ByteArray>? {
|
): List<ByteArray>? {
|
||||||
|
|
||||||
val md = MessageDigest.getInstance(hashAlgorithm)
|
val md = MessageDigest.getInstance(hashAlgorithm)
|
||||||
val digestLength = md.getDigestLength()
|
val digestLength = md.getDigestLength()
|
||||||
val targetKeySize = keyLength + ivLength
|
val targetKeySize = keyLength + ivLength
|
||||||
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
md.reset()
|
md.reset()
|
||||||
|
|
||||||
while (generatedLength < targetKeySize) {
|
while (generatedLength < targetKeySize) {
|
||||||
if (generatedLength > 0)
|
if (generatedLength > 0) {
|
||||||
md.update(
|
md.update(
|
||||||
generatedData,
|
generatedData,
|
||||||
generatedLength - digestLength,
|
generatedLength - digestLength,
|
||||||
digestLength
|
digestLength,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
md.update(password)
|
md.update(password)
|
||||||
md.update(salt, 0, 8)
|
md.update(salt, 0, 8)
|
||||||
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
val result = listOf(
|
val result = listOf(
|
||||||
generatedData.copyOfRange(0, keyLength),
|
generatedData.copyOfRange(0, keyLength),
|
||||||
generatedData.copyOfRange(keyLength, targetKeySize)
|
generatedData.copyOfRange(keyLength, targetKeySize),
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
} catch (e: DigestException) {
|
} catch (e: DigestException) {
|
||||||
|
@ -17,7 +17,7 @@ class MoshahdaExtractor(private val client: OkHttpClient) {
|
|||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
for (source in sources) {
|
for (source in sources) {
|
||||||
val masterUrl = source.substringBefore("\"}")
|
val masterUrl = source.substringBefore("\"}")
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
|
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
|
||||||
.forEach {
|
.forEach {
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -7,7 +7,7 @@ import okhttp3.OkHttpClient
|
|||||||
|
|
||||||
class VidYardExtractor(private val client: OkHttpClient) {
|
class VidYardExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String, headers: Headers): List<Video> {
|
fun videosFromUrl(url: String, headers: Headers): List<Video> {
|
||||||
val callPlayer = client.newCall(GET(url)).execute().body!!.string()
|
val callPlayer = client.newCall(GET(url)).execute().body.string()
|
||||||
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
|
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
|
||||||
val sources = data.split("profile\":\"").drop(1)
|
val sources = data.split("profile\":\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -75,7 +75,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.setUrlWithoutDomain(document.location())
|
episode.setUrlWithoutDomain(document.location())
|
||||||
episode.episode_number = 1F
|
episode.episode_number = 1F
|
||||||
episode.name = document.selectFirst("div.name.col-xs-12 span h1").text()
|
episode.name = document.selectFirst("div.name.col-xs-12 span h1")!!.text()
|
||||||
return listOf(episode)
|
return listOf(episode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val iframe = document.selectFirst("iframe").attr("src")
|
val iframe = document.selectFirst("iframe")!!.attr("src")
|
||||||
val referer = response.request.url.encodedPath
|
val referer = response.request.url.encodedPath
|
||||||
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
||||||
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
||||||
@ -146,7 +146,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.thumbnail_url = baseUrl + element.select("img").first().attr("data-original")
|
anime.thumbnail_url = baseUrl + element.selectFirst("img")!!.attr("data-original")
|
||||||
anime.title = element.select("img").attr("alt").replace(" poster", "")
|
anime.title = element.select("img").attr("alt").replace(" poster", "")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
@ -168,6 +168,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return GET(genreUrl.toString(), headers)
|
return GET(genreUrl.toString(), headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw Exception("اختر فلتر")
|
throw Exception("اختر فلتر")
|
||||||
@ -224,7 +225,7 @@ class AnimeBlkom : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Type(" قائمة الأفلام ", "movie-list"),
|
Type(" قائمة الأفلام ", "movie-list"),
|
||||||
Type(" قائمة الأوفا ", "ova-list"),
|
Type(" قائمة الأوفا ", "ova-list"),
|
||||||
Type(" قائمة الأونا ", "ona-list"),
|
Type(" قائمة الأونا ", "ona-list"),
|
||||||
Type(" قائمة الحلقات خاصة ", "special-list")
|
Type(" قائمة الحلقات خاصة ", "special-list"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// preferred quality settings
|
// preferred quality settings
|
||||||
|
@ -100,7 +100,7 @@ class AnimeLek : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val url = element.attr("data-ep-url")
|
val url = element.attr("data-ep-url")
|
||||||
val qualityy = element.text()
|
val qualityy = element.text()
|
||||||
val location = element.ownerDocument().location()
|
val location = element.ownerDocument()!!.location()
|
||||||
val videoHeaders = Headers.headersOf("Referer", location)
|
val videoHeaders = Headers.headersOf("Referer", location)
|
||||||
when {
|
when {
|
||||||
url.contains("sbembed.com") || url.contains("sbembed1.com") || url.contains("sbplay.org") ||
|
url.contains("sbembed.com") || url.contains("sbembed1.com") || url.contains("sbplay.org") ||
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -81,9 +81,9 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
if (type.contains("animes")) {
|
if (type.contains("animes")) {
|
||||||
val seasonsHtml = client.newCall(
|
val seasonsHtml = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
seriesLink
|
seriesLink,
|
||||||
// headers = Headers.headersOf("Referer", document.location())
|
// headers = Headers.headersOf("Referer", document.location())
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
val seasonsElements = seasonsHtml.select("ul.chapters-list li a.title")
|
val seasonsElements = seasonsHtml.select("ul.chapters-list li a.title")
|
||||||
seasonsElements.reversed().forEach {
|
seasonsElements.reversed().forEach {
|
||||||
@ -107,7 +107,7 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val episodesHtml = client.newCall(
|
val episodesHtml = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
episodesUrl,
|
episodesUrl,
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("ul.chapters-list li")
|
val episodeElements = episodesHtml.select("ul.chapters-list li")
|
||||||
return episodeElements.map { episodeFromElement(it) }
|
return episodeElements.map { episodeFromElement(it) }
|
||||||
@ -121,8 +121,8 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
else -> 1F
|
else -> 1F
|
||||||
}
|
}
|
||||||
// element.select("td > span.Num").text().toFloat()
|
// element.select("td > span.Num").text().toFloat()
|
||||||
// val SeasonNum = element.ownerDocument().select("div.Title span").text()
|
// val SeasonNum = element.ownerDocument()!!.select("div.Title span").text()
|
||||||
val seasonName = element.ownerDocument().select("div.media-title h1").text()
|
val seasonName = element.ownerDocument()!!.select("div.media-title h1").text()
|
||||||
episode.name = "$seasonName : " + element.select("a.title h3").text()
|
episode.name = "$seasonName : " + element.select("a.title h3").text()
|
||||||
episode.setUrlWithoutDomain(element.select("a.title").attr("abs:href"))
|
episode.setUrlWithoutDomain(element.select("a.title").attr("abs:href"))
|
||||||
return episode
|
return episode
|
||||||
@ -145,7 +145,7 @@ class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val elements = document.select(videoListSelector())
|
val elements = document.select(videoListSelector())
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val location = element.ownerDocument().location()
|
val location = element.ownerDocument()!!.location()
|
||||||
val videoHeaders = Headers.headersOf("Referer", location)
|
val videoHeaders = Headers.headersOf("Referer", location)
|
||||||
val qualityy = element.text()
|
val qualityy = element.text()
|
||||||
val post = element.attr("data-post")
|
val post = element.attr("data-post")
|
||||||
|
@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
||||||
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
||||||
val sojson = REGEX_SOJSON.getFirst(eval)
|
val sojson = REGEX_SOJSON.getFirst(eval)
|
||||||
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
hashAlgorithm: String = "MD5",
|
hashAlgorithm: String = "MD5",
|
||||||
keyLength: Int = 32,
|
keyLength: Int = 32,
|
||||||
ivLength: Int = 16,
|
ivLength: Int = 16,
|
||||||
iterations: Int = 1
|
iterations: Int = 1,
|
||||||
): List<ByteArray>? {
|
): List<ByteArray>? {
|
||||||
|
|
||||||
val md = MessageDigest.getInstance(hashAlgorithm)
|
val md = MessageDigest.getInstance(hashAlgorithm)
|
||||||
val digestLength = md.getDigestLength()
|
val digestLength = md.getDigestLength()
|
||||||
val targetKeySize = keyLength + ivLength
|
val targetKeySize = keyLength + ivLength
|
||||||
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
md.reset()
|
md.reset()
|
||||||
|
|
||||||
while (generatedLength < targetKeySize) {
|
while (generatedLength < targetKeySize) {
|
||||||
if (generatedLength > 0)
|
if (generatedLength > 0) {
|
||||||
md.update(
|
md.update(
|
||||||
generatedData,
|
generatedData,
|
||||||
generatedLength - digestLength,
|
generatedLength - digestLength,
|
||||||
digestLength
|
digestLength,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
md.update(password)
|
md.update(password)
|
||||||
md.update(salt, 0, 8)
|
md.update(salt, 0, 8)
|
||||||
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
val result = listOf(
|
val result = listOf(
|
||||||
generatedData.copyOfRange(0, keyLength),
|
generatedData.copyOfRange(0, keyLength),
|
||||||
generatedData.copyOfRange(keyLength, targetKeySize)
|
generatedData.copyOfRange(keyLength, targetKeySize),
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
} catch (e: DigestException) {
|
} catch (e: DigestException) {
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class UQLoadExtractor(private val client: OkHttpClient) {
|
class UQLoadExtractor(private val client: OkHttpClient) {
|
||||||
fun videoFromUrl(url: String, quality: String): Video? {
|
fun videoFromUrl(url: String, quality: String): Video? {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val check = document.selectFirst("script:containsData(sources)").data()
|
val check = document.selectFirst("script:containsData(sources)")!!.data()
|
||||||
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
||||||
return if (check.contains("sources")) {
|
return if (check.contains("sources")) {
|
||||||
Video(url, quality, videoUrl)
|
Video(url, quality, videoUrl)
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -79,12 +79,12 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val epNum = getNumberFromEpsString(element.select(".epl-num").text())
|
val epNum = getNumberFromEpsString(element.select(".epl-num").text())
|
||||||
val urlElements = element.select("a")
|
val urlElements = element.select("a")
|
||||||
episode.setUrlWithoutDomain(urlElements.attr("href"))
|
episode.setUrlWithoutDomain(urlElements.attr("href"))
|
||||||
episode.name = element.select(".epl-title").text().ifBlank { urlElements.first().text() }
|
episode.name = element.select(".epl-title").text().ifBlank { urlElements.first()!!.text() }
|
||||||
episode.episode_number = when {
|
episode.episode_number = when {
|
||||||
(epNum.isNotEmpty()) -> epNum.toFloat()
|
(epNum.isNotEmpty()) -> epNum.toFloat()
|
||||||
else -> 1F
|
else -> 1F
|
||||||
}
|
}
|
||||||
episode.date_upload = element.select(".epl-date").first()?.text()?.let { parseEpisodeDate(it) } ?: 0L
|
episode.date_upload = element.selectFirst(".epl-date")?.text()?.let { parseEpisodeDate(it) } ?: 0L
|
||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val elements = document.select(videoListSelector())
|
val elements = document.select(videoListSelector())
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val location = element.ownerDocument().location()
|
val location = element.ownerDocument()!!.location()
|
||||||
val videoEncode = element.attr("value")
|
val videoEncode = element.attr("value")
|
||||||
val qualityy = element.text()
|
val qualityy = element.text()
|
||||||
val decoder: Base64.Decoder = Base64.getDecoder()
|
val decoder: Base64.Decoder = Base64.getDecoder()
|
||||||
@ -256,7 +256,7 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
return fetchAnimeDetails(
|
return fetchAnimeDetails(
|
||||||
SAnime.create()
|
SAnime.create()
|
||||||
.apply { this.url = "$AnimeUrlDirectory/$AnimePath" }
|
.apply { this.url = "$AnimeUrlDirectory/$AnimePath" },
|
||||||
)
|
)
|
||||||
.map {
|
.map {
|
||||||
// Isn't set in returned Anime
|
// Isn't set in returned Anime
|
||||||
@ -371,8 +371,11 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
seriesDetails.selectFirst(seriesTypeSelector)?.ownText().takeIf { it.isNullOrBlank().not() }?.let { genres.add(it) }
|
seriesDetails.selectFirst(seriesTypeSelector)?.ownText().takeIf { it.isNullOrBlank().not() }?.let { genres.add(it) }
|
||||||
genre = genres.map { genre ->
|
genre = genres.map { genre ->
|
||||||
genre.lowercase(Locale.forLanguageTag(lang)).replaceFirstChar { char ->
|
genre.lowercase(Locale.forLanguageTag(lang)).replaceFirstChar { char ->
|
||||||
if (char.isLowerCase()) char.titlecase(Locale.forLanguageTag(lang))
|
if (char.isLowerCase()) {
|
||||||
else char.toString()
|
char.titlecase(Locale.forLanguageTag(lang))
|
||||||
|
} else {
|
||||||
|
char.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.joinToString { it.trim() }
|
.joinToString { it.trim() }
|
||||||
@ -402,11 +405,11 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
open class SelectFilter(
|
open class SelectFilter(
|
||||||
displayName: String,
|
displayName: String,
|
||||||
val vals: Array<Pair<String, String>>,
|
val vals: Array<Pair<String, String>>,
|
||||||
defaultValue: String? = null
|
defaultValue: String? = null,
|
||||||
) : AnimeFilter.Select<String>(
|
) : AnimeFilter.Select<String>(
|
||||||
displayName,
|
displayName,
|
||||||
vals.map { it.first }.toTypedArray(),
|
vals.map { it.first }.toTypedArray(),
|
||||||
vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0
|
vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0,
|
||||||
) {
|
) {
|
||||||
fun selectedValue() = vals[state].second
|
fun selectedValue() = vals[state].second
|
||||||
}
|
}
|
||||||
@ -418,8 +421,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("Ongoing", "ongoing"),
|
Pair("Ongoing", "ongoing"),
|
||||||
Pair("Completed", "completed"),
|
Pair("Completed", "completed"),
|
||||||
Pair("Hiatus", "hiatus"),
|
Pair("Hiatus", "hiatus"),
|
||||||
Pair("UpComing", "upcoming")
|
Pair("UpComing", "upcoming"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class TypeFilter : SelectFilter(
|
protected class TypeFilter : SelectFilter(
|
||||||
@ -430,8 +433,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("Movie", "movie"),
|
Pair("Movie", "movie"),
|
||||||
Pair("OVA", "ova"),
|
Pair("OVA", "ova"),
|
||||||
Pair("ONA", "ona"),
|
Pair("ONA", "ona"),
|
||||||
Pair("Special", "special")
|
Pair("Special", "special"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class OrderByFilter(defaultOrder: String? = null) : SelectFilter(
|
protected class OrderByFilter(defaultOrder: String? = null) : SelectFilter(
|
||||||
@ -442,9 +445,9 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("Z-A", "titlereverse"),
|
Pair("Z-A", "titlereverse"),
|
||||||
Pair("Latest Update", "update"),
|
Pair("Latest Update", "update"),
|
||||||
Pair("Latest Added", "latest"),
|
Pair("Latest Added", "latest"),
|
||||||
Pair("Popular", "popular")
|
Pair("Popular", "popular"),
|
||||||
),
|
),
|
||||||
defaultOrder
|
defaultOrder,
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class Genre(name: String, val value: String) : AnimeFilter.TriState(name)
|
protected class Genre(name: String, val value: String) : AnimeFilter.TriState(name)
|
||||||
@ -532,8 +535,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun parseGenres(document: Document): List<Genre>? {
|
private fun parseGenres(document: Document): List<Genre>? {
|
||||||
return document.selectFirst("div.filter:contains(التصنيف) ul.scrollz")?.select("li")?.map { li ->
|
return document.selectFirst("div.filter:contains(التصنيف) ul.scrollz")?.select("li")?.map { li ->
|
||||||
Genre(
|
Genre(
|
||||||
li.selectFirst("label").text(),
|
li.selectFirst("label")!!.text(),
|
||||||
li.selectFirst("input[type=checkbox]").attr("value")
|
li.selectFirst("input[type=checkbox]")!!.attr("value"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,8 +544,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun parseSeasons(document: Document): List<Season>? {
|
private fun parseSeasons(document: Document): List<Season>? {
|
||||||
return document.selectFirst("div.filter:contains(الموسم) ul.scrollz")?.select("li")?.map { li ->
|
return document.selectFirst("div.filter:contains(الموسم) ul.scrollz")?.select("li")?.map { li ->
|
||||||
Season(
|
Season(
|
||||||
li.selectFirst("label").text(),
|
li.selectFirst("label")!!.text(),
|
||||||
li.selectFirst("input[type=checkbox]").attr("value")
|
li.selectFirst("input[type=checkbox]")!!.attr("value"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,8 +553,8 @@ class AnimeTitans : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun parseStudios(document: Document): List<Studio>? {
|
private fun parseStudios(document: Document): List<Studio>? {
|
||||||
return document.selectFirst("div.filter:contains(الاستديو) ul.scrollz")?.select("li")?.map { li ->
|
return document.selectFirst("div.filter:contains(الاستديو) ul.scrollz")?.select("li")?.map { li ->
|
||||||
Studio(
|
Studio(
|
||||||
li.selectFirst("label").text(),
|
li.selectFirst("label")!!.text(),
|
||||||
li.selectFirst("input[type=checkbox]").attr("value")
|
li.selectFirst("input[type=checkbox]")!!.attr("value"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class AnimeTitansExtractor(private val client: OkHttpClient) {
|
|||||||
val callPlayer = client.newCall(GET(url)).execute().asJsoup()
|
val callPlayer = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val masterUrl = callPlayer.data().substringAfter("source: \"").substringBefore("\",")
|
val masterUrl = callPlayer.data().substringAfter("source: \"").substringBefore("\",")
|
||||||
val domain = masterUrl.substringBefore("/videowl") // .replace("https", "http")
|
val domain = masterUrl.substringBefore("/videowl") // .replace("https", "http")
|
||||||
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body!!.string()
|
val masterPlaylist = client.newCall(GET(masterUrl)).execute().body.string()
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
|
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
|
||||||
val quality = "AnimeTitans: " + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore("\n") + "p"
|
val quality = "AnimeTitans: " + it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore("\n") + "p"
|
||||||
|
@ -19,7 +19,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
|
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
val body = client.newCall(GET(url.replace(".me", ".to"))).execute()
|
||||||
.body!!.string()
|
.body.string()
|
||||||
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
val eval = JsUnpacker.unpackAndCombine(body)!!.replace("\\", "")
|
||||||
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
val json = Json.decodeFromString<JsonObject>(REGEX_DATAJSON.getFirst(eval))
|
||||||
val sojson = REGEX_SOJSON.getFirst(eval)
|
val sojson = REGEX_SOJSON.getFirst(eval)
|
||||||
@ -61,9 +61,8 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
hashAlgorithm: String = "MD5",
|
hashAlgorithm: String = "MD5",
|
||||||
keyLength: Int = 32,
|
keyLength: Int = 32,
|
||||||
ivLength: Int = 16,
|
ivLength: Int = 16,
|
||||||
iterations: Int = 1
|
iterations: Int = 1,
|
||||||
): List<ByteArray>? {
|
): List<ByteArray>? {
|
||||||
|
|
||||||
val md = MessageDigest.getInstance(hashAlgorithm)
|
val md = MessageDigest.getInstance(hashAlgorithm)
|
||||||
val digestLength = md.getDigestLength()
|
val digestLength = md.getDigestLength()
|
||||||
val targetKeySize = keyLength + ivLength
|
val targetKeySize = keyLength + ivLength
|
||||||
@ -75,12 +74,13 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
md.reset()
|
md.reset()
|
||||||
|
|
||||||
while (generatedLength < targetKeySize) {
|
while (generatedLength < targetKeySize) {
|
||||||
if (generatedLength > 0)
|
if (generatedLength > 0) {
|
||||||
md.update(
|
md.update(
|
||||||
generatedData,
|
generatedData,
|
||||||
generatedLength - digestLength,
|
generatedLength - digestLength,
|
||||||
digestLength
|
digestLength,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
md.update(password)
|
md.update(password)
|
||||||
md.update(salt, 0, 8)
|
md.update(salt, 0, 8)
|
||||||
@ -95,7 +95,7 @@ class GdrivePlayerExtractor(private val client: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
val result = listOf(
|
val result = listOf(
|
||||||
generatedData.copyOfRange(0, keyLength),
|
generatedData.copyOfRange(0, keyLength),
|
||||||
generatedData.copyOfRange(keyLength, targetKeySize)
|
generatedData.copyOfRange(keyLength, targetKeySize),
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
} catch (e: DigestException) {
|
} catch (e: DigestException) {
|
||||||
|
@ -17,7 +17,7 @@ class Mp4uploadExtractor {
|
|||||||
"referer" to url,
|
"referer" to url,
|
||||||
"method_free" to "+",
|
"method_free" to "+",
|
||||||
"method_premiun" to "",
|
"method_premiun" to "",
|
||||||
)
|
),
|
||||||
).method(Connection.Method.POST).ignoreContentType(true)
|
).method(Connection.Method.POST).ignoreContentType(true)
|
||||||
.ignoreHttpErrors(true).execute().url().toString()
|
.ignoreHttpErrors(true).execute().url().toString()
|
||||||
Video(videoUrl, "Mp4Upload", videoUrl, headers)
|
Video(videoUrl, "Mp4Upload", videoUrl, headers)
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -7,7 +7,7 @@ import okhttp3.OkHttpClient
|
|||||||
|
|
||||||
class VidYardExtractor(private val client: OkHttpClient) {
|
class VidYardExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String, headers: Headers): List<Video> {
|
fun videosFromUrl(url: String, headers: Headers): List<Video> {
|
||||||
val callPlayer = client.newCall(GET(url)).execute().body!!.string()
|
val callPlayer = client.newCall(GET(url)).execute().body.string()
|
||||||
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
|
val data = callPlayer.substringAfter("hls\":[").substringBefore("]")
|
||||||
val sources = data.split("profile\":\"").drop(1)
|
val sources = data.split("profile\":\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -153,7 +153,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.title = element.select("div.BlockName h4").text()
|
anime.title = element.select("div.BlockName h4").text()
|
||||||
anime.thumbnail_url = element.select("img").first().attr("data-src")
|
anime.thumbnail_url = element.selectFirst("img")!!.attr("data-src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +174,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return GET(typeUrl.toString(), headers)
|
return GET(typeUrl.toString(), headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw Exception("اختر فلتر")
|
throw Exception("اختر فلتر")
|
||||||
@ -185,7 +186,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.thumbnail_url = document.select("div.Poster img").first().attr("data-src")
|
anime.thumbnail_url = document.selectFirst("div.Poster img")!!.attr("data-src")
|
||||||
anime.title = document.select("div.BreadCrumbs ol li:last-child a span").text().replace(" مترجم", "").replace("فيلم ", "")
|
anime.title = document.select("div.BreadCrumbs ol li:last-child a span").text().replace(" مترجم", "").replace("فيلم ", "")
|
||||||
anime.genre = document.select("div.MetaTermsInfo > li:contains(النوع) > a").joinToString(", ") { it.text() }
|
anime.genre = document.select("div.MetaTermsInfo > li:contains(النوع) > a").joinToString(", ") { it.text() }
|
||||||
anime.description = document.select("div.StoryLine p").text()
|
anime.description = document.select("div.StoryLine p").text()
|
||||||
@ -225,7 +226,7 @@ class ArabSeed : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Type("مسلسلات رمضان 2019", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2019/"),
|
Type("مسلسلات رمضان 2019", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2019/"),
|
||||||
Type("مسلسلات رمضان 2020", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2020-hd/"),
|
Type("مسلسلات رمضان 2020", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2020-hd/"),
|
||||||
Type("مسلسلات رمضان 2021", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2021/"),
|
Type("مسلسلات رمضان 2021", "%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-%d8%b1%d9%85%d8%b6%d8%a7%d9%86-2021/"),
|
||||||
Type("مسلسلات Netfilx", "netfilx/%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-netfilz/")
|
Type("مسلسلات Netfilx", "netfilx/%d9%85%d8%b3%d9%84%d8%b3%d9%84%d8%a7%d8%aa-netfilz/"),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
// anime.thumbnail_url = element.select("div.image img").first().attr("data-src")
|
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListRequest(episode: SEpisode): Request {
|
override fun videoListRequest(episode: SEpisode): Request {
|
||||||
val document = client.newCall(GET(baseUrl + episode.url)).execute().asJsoup()
|
val document = client.newCall(GET(baseUrl + episode.url)).execute().asJsoup()
|
||||||
val link = document.selectFirst("div.loop-episode a.current").attr("href")
|
val link = document.selectFirst("div.loop-episode a.current")!!.attr("href")
|
||||||
return GET(link)
|
return GET(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +104,9 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
} else if (url.contains("yodbox")) {
|
} else if (url.contains("yodbox")) {
|
||||||
val html = client.newCall(GET(url)).execute().asJsoup()
|
val html = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val videoFromURL = html.select("source").attr("src")
|
val videoFromURL = html.select("source").attr("src")
|
||||||
if (videoFromURL.isNotEmpty())
|
if (videoFromURL.isNotEmpty()) {
|
||||||
videoList.add(Video(videoFromURL, "Yodbox: mirror", videoFromURL))
|
videoList.add(Video(videoFromURL, "Yodbox: mirror", videoFromURL))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return videoList
|
return videoList
|
||||||
@ -142,7 +143,7 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
// anime.thumbnail_url = element.select("div.image img").first().attr("data-src")
|
// anime.thumbnail_url = element.selectFirst("div.image img")!!.attr("data-src")
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
@ -223,14 +224,14 @@ class Asia2TV : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Type("الدراما اليابانية", "japanese"),
|
Type("الدراما اليابانية", "japanese"),
|
||||||
Type("الدراما الصينية والتايوانية", "chinese-taiwanese"),
|
Type("الدراما الصينية والتايوانية", "chinese-taiwanese"),
|
||||||
Type("الدراما التايلاندية", "thai"),
|
Type("الدراما التايلاندية", "thai"),
|
||||||
Type("برامج الترفيه", "kshow")
|
Type("برامج الترفيه", "kshow"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getStatusList() = listOf(
|
private fun getStatusList() = listOf(
|
||||||
Status("أختر", ""),
|
Status("أختر", ""),
|
||||||
Status("يبث حاليا", "status/ongoing-drama"),
|
Status("يبث حاليا", "status/ongoing-drama"),
|
||||||
Status("الدراما المكتملة", "completed-dramas"),
|
Status("الدراما المكتملة", "completed-dramas"),
|
||||||
Status("الدراما القادمة", "status/upcoming-drama")
|
Status("الدراما القادمة", "status/upcoming-drama"),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class Cartoons4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun episodeFromElement(element: Element): SEpisode {
|
override fun episodeFromElement(element: Element): SEpisode {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.setUrlWithoutDomain(element.attr("href"))
|
episode.setUrlWithoutDomain(element.attr("href"))
|
||||||
episode.name = element.ownerDocument().select("header.Top h1").text()
|
episode.name = element.ownerDocument()!!.select("header.Top h1").text()
|
||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,14 +76,14 @@ class Cartoons4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val refererHeaders = Headers.headersOf("referer", referer)
|
val refererHeaders = Headers.headersOf("referer", referer)
|
||||||
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
|
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
|
||||||
.execute().asJsoup()
|
.execute().asJsoup()
|
||||||
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
|
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun videoListSelector() = "a.button.is-success"
|
override fun videoListSelector() = "a.button.is-success"
|
||||||
|
|
||||||
private fun videosFromElement(element: Element): List<Video> {
|
private fun videosFromElement(element: Element): List<Video> {
|
||||||
val vidURL = element.attr("abs:href")
|
val vidURL = element.attr("abs:href")
|
||||||
val apiCall = client.newCall(POST(vidURL.replace("/v/", "/api/source/"))).execute().body!!.string()
|
val apiCall = client.newCall(POST(vidURL.replace("/v/", "/api/source/"))).execute().body.string()
|
||||||
val data = apiCall.substringAfter("\"data\":[").substringBefore("],")
|
val data = apiCall.substringAfter("\"data\":[").substringBefore("],")
|
||||||
val sources = data.split("\"file\":\"").drop(1)
|
val sources = data.split("\"file\":\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
|
@ -77,20 +77,20 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
fun addEpisodes(document: Document) {
|
fun addEpisodes(document: Document) {
|
||||||
if (document.select(episodeListSelector()).isNullOrEmpty())
|
if (document.select(episodeListSelector()).isNullOrEmpty()) {
|
||||||
document.select("div.shortLink").map { episodes.add(episodeExtract(it)) }
|
document.select("div.shortLink").map { episodes.add(episodeExtract(it)) }
|
||||||
else {
|
} else {
|
||||||
document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) }
|
document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) }
|
||||||
document.select(seasonsNextPageSelector(seasonNumber)).firstOrNull()?.let {
|
document.selectFirst(seasonsNextPageSelector(seasonNumber))?.let {
|
||||||
seasonNumber++
|
seasonNumber++
|
||||||
addEpisodes(
|
addEpisodes(
|
||||||
client.newCall(
|
client.newCall(
|
||||||
GET(
|
GET(
|
||||||
"$baseUrl/?p=" + it.select("div.seasonDiv")
|
"$baseUrl/?p=" + it.select("div.seasonDiv")
|
||||||
.attr("data-href"),
|
.attr("data-href"),
|
||||||
headers
|
headers,
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun episodeFromElement(element: Element): SEpisode {
|
override fun episodeFromElement(element: Element): SEpisode {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.setUrlWithoutDomain(element.attr("abs:href"))
|
episode.setUrlWithoutDomain(element.attr("abs:href"))
|
||||||
episode.name = element.ownerDocument().select("div.seasonDiv.active > div.title").text() + " : " + element.text()
|
episode.name = element.ownerDocument()!!.select("div.seasonDiv.active > div.title").text() + " : " + element.text()
|
||||||
episode.episode_number = element.text().replace("الحلقة ", "").toFloat()
|
episode.episode_number = element.text().replace("الحلقة ", "").toFloat()
|
||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
@ -116,9 +116,9 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val getSources = "master.m3u8"
|
val getSources = "master.m3u8"
|
||||||
val referer = Headers.headersOf("Referer", "$baseUrl/")
|
val referer = Headers.headersOf("Referer", "$baseUrl/")
|
||||||
val iframe = document.selectFirst("iframe").attr("src").substringBefore("&img")
|
val iframe = document.selectFirst("iframe")!!.attr("src").substringBefore("&img")
|
||||||
val webViewIncpec = client.newBuilder().addInterceptor(GetSourcesInterceptor(getSources, client)).build()
|
val webViewIncpec = client.newBuilder().addInterceptor(GetSourcesInterceptor(getSources, client)).build()
|
||||||
val lol = webViewIncpec.newCall(GET(iframe, referer)).execute().body!!.string()
|
val lol = webViewIncpec.newCall(GET(iframe, referer)).execute().body.string()
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
lol.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
|
lol.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
|
||||||
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
|
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
|
||||||
@ -229,7 +229,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun getFilterList() = AnimeFilterList(
|
override fun getFilterList() = AnimeFilterList(
|
||||||
AnimeFilter.Header("NOTE: Ignored if using text search!"),
|
AnimeFilter.Header("NOTE: Ignored if using text search!"),
|
||||||
AnimeFilter.Separator(),
|
AnimeFilter.Separator(),
|
||||||
GenreFilter(getGenreList())
|
GenreFilter(getGenreList()),
|
||||||
)
|
)
|
||||||
|
|
||||||
private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("تصنيف المسلسلات", vals)
|
private class GenreFilter(vals: Array<Pair<String, String>>) : UriPartFilter("تصنيف المسلسلات", vals)
|
||||||
@ -251,7 +251,7 @@ class FASELHD : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("المسلسلات القصيرة", "short_series"),
|
Pair("المسلسلات القصيرة", "short_series"),
|
||||||
Pair("المسلسلات الاسيوية", "asian-series"),
|
Pair("المسلسلات الاسيوية", "asian-series"),
|
||||||
Pair("المسلسلات الاسيوية الاعلي مشاهدة", "asian_top_views"),
|
Pair("المسلسلات الاسيوية الاعلي مشاهدة", "asian_top_views"),
|
||||||
Pair("الانمي الاعلي مشاهدة", "anime_top_views")
|
Pair("الانمي الاعلي مشاهدة", "anime_top_views"),
|
||||||
)
|
)
|
||||||
|
|
||||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
|
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
|
||||||
|
@ -68,7 +68,7 @@ class GetSourcesInterceptor(private val getSources: String, private val client:
|
|||||||
webview.webViewClient = object : WebViewClient() {
|
webview.webViewClient = object : WebViewClient() {
|
||||||
override fun shouldInterceptRequest(
|
override fun shouldInterceptRequest(
|
||||||
view: WebView,
|
view: WebView,
|
||||||
request: WebResourceRequest
|
request: WebResourceRequest,
|
||||||
): WebResourceResponse? {
|
): WebResourceResponse? {
|
||||||
val url = request.url.toString()
|
val url = request.url.toString()
|
||||||
if (url.contains(getSources)) {
|
if (url.contains(getSources)) {
|
||||||
|
@ -74,8 +74,8 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val seasonsHtml = client.newCall(
|
val seasonsHtml = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
seasonUrl,
|
seasonUrl,
|
||||||
headers = Headers.headersOf("Referer", document.location())
|
headers = Headers.headersOf("Referer", document.location()),
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
val seasonsElements = seasonsHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2 div.card")
|
val seasonsElements = seasonsHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2 div.card")
|
||||||
seasonsElements.forEach {
|
seasonsElements.forEach {
|
||||||
@ -101,7 +101,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val episodesHtml = client.newCall(
|
val episodesHtml = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
seasonId,
|
seasonId,
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
val episodeElements = episodesHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2")
|
val episodeElements = episodesHtml.select("div.col-6.col-sm-4.col-md-3.col-xl-2")
|
||||||
return episodeElements.map { episodeFromElement(it, seasonName) }
|
return episodeElements.map { episodeFromElement(it, seasonName) }
|
||||||
@ -110,7 +110,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun episodeFromElement(element: Element, seasonName: String): SEpisode {
|
private fun episodeFromElement(element: Element, seasonName: String): SEpisode {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.episode_number = element.select("h3.card__title a").attr("abs:href").substringAfter("episode-").toFloat()
|
episode.episode_number = element.select("h3.card__title a").attr("abs:href").substringAfter("episode-").toFloat()
|
||||||
// val SeasonNum = element.ownerDocument().select("div.Title span").text()
|
// val SeasonNum = element.ownerDocument()!!.select("div.Title span").text()
|
||||||
episode.name = "$seasonName" + ": " + element.select("h3.card__title a").text()
|
episode.name = "$seasonName" + ": " + element.select("h3.card__title a").text()
|
||||||
episode.setUrlWithoutDomain(element.select("h3.card__title a").attr("abs:href"))
|
episode.setUrlWithoutDomain(element.select("h3.card__title a").attr("abs:href"))
|
||||||
return episode
|
return episode
|
||||||
@ -120,15 +120,15 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val iframe1 = client.newCall(GET(document.selectFirst("iframe#video").attr("data-src")))
|
val iframe1 = client.newCall(GET(document.selectFirst("iframe#video")!!.attr("data-src")))
|
||||||
.execute().asJsoup()
|
.execute().asJsoup()
|
||||||
val iframe = iframe1.selectFirst("iframe").attr("src")
|
val iframe = iframe1.selectFirst("iframe")!!.attr("src")
|
||||||
|
|
||||||
val referer = response.request.url.encodedPath
|
val referer = response.request.url.encodedPath
|
||||||
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
val newHeaders = Headers.headersOf("referer", baseUrl + referer)
|
||||||
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
val iframeResponse = client.newCall(GET(iframe, newHeaders))
|
||||||
.execute().asJsoup()
|
.execute().asJsoup()
|
||||||
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
|
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun videoListSelector() = "script:containsData(source)"
|
override fun videoListSelector() = "script:containsData(source)"
|
||||||
@ -217,6 +217,7 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
return GET(genreUrl, headers)
|
return GET(genreUrl, headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw Exception("اختر فلتر")
|
throw Exception("اختر فلتر")
|
||||||
@ -321,6 +322,6 @@ class Movies4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Genre("كوميديا", "38"),
|
Genre("كوميديا", "38"),
|
||||||
Genre("مغامرة", "39"),
|
Genre("مغامرة", "39"),
|
||||||
Genre("موسيقية", "40"),
|
Genre("موسيقية", "40"),
|
||||||
Genre("وثائقية", "41")
|
Genre("وثائقية", "41"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -73,16 +73,17 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
fun addEpisodeNew(url: String, type: String, title: String = "") {
|
fun addEpisodeNew(url: String, type: String, title: String = "") {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.setUrlWithoutDomain(url)
|
episode.setUrlWithoutDomain(url)
|
||||||
if (type == "assembly")
|
if (type == "assembly") {
|
||||||
episode.name = title.replace("فيلم", "").trim()
|
episode.name = title.replace("فيلم", "").trim()
|
||||||
else if (type == "movie")
|
} else if (type == "movie") {
|
||||||
episode.name = "مشاهدة"
|
episode.name = "مشاهدة"
|
||||||
else if (TitleRegex.SEASON.containsMatchIn(title))
|
} else if (TitleRegex.SEASON.containsMatchIn(title)) {
|
||||||
episode.name = TitleRegex.SEASON.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
|
episode.name = TitleRegex.SEASON.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
|
||||||
else if (TitleRegex.EPISODE.containsMatchIn(title))
|
} else if (TitleRegex.EPISODE.containsMatchIn(title)) {
|
||||||
episode.name = TitleRegex.EPISODE.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
|
episode.name = TitleRegex.EPISODE.find(title)!!.value.replace("مترجمة", "").replace("والاخيرة", "").trim()
|
||||||
else
|
} else {
|
||||||
episode.name = title
|
episode.name = title
|
||||||
|
}
|
||||||
|
|
||||||
episodes.add(episode)
|
episodes.add(episode)
|
||||||
}
|
}
|
||||||
@ -146,11 +147,12 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
addEpisodeNew(
|
addEpisodeNew(
|
||||||
episode.select("a").attr("href"),
|
episode.select("a").attr("href"),
|
||||||
"series",
|
"series",
|
||||||
document.select("div.SeriesSingle h2 span a").text() + " " + episode.select("a").text()
|
document.select("div.SeriesSingle h2 span a").text() + " " + episode.select("a").text(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
addEpisodeNew(url, "movie")
|
addEpisodeNew(url, "movie")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,8 +224,10 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
val title = titleEdit(element.select("a div.BlockImageItem div.BlockTitle").text(), true)
|
val title = titleEdit(element.select("a div.BlockImageItem div.BlockTitle").text(), true)
|
||||||
anime.thumbnail_url = element.select("a div.BlockImageItem img").attr("src")
|
anime.thumbnail_url = element.select("a div.BlockImageItem img").attr("src")
|
||||||
if (anime.thumbnail_url.isNullOrEmpty()) anime.thumbnail_url =
|
if (anime.thumbnail_url.isNullOrEmpty()) {
|
||||||
element.select("a div.BlockImageItem img").attr("data-src")
|
anime.thumbnail_url =
|
||||||
|
element.select("a div.BlockImageItem img").attr("data-src")
|
||||||
|
}
|
||||||
anime.setUrlWithoutDomain(element.select("a").attr("href"))
|
anime.setUrlWithoutDomain(element.select("a").attr("href"))
|
||||||
anime.title = title
|
anime.title = title
|
||||||
return anime
|
return anime
|
||||||
@ -260,8 +264,11 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
// div.CoverSingle div.CoverSingleContent
|
// div.CoverSingle div.CoverSingleContent
|
||||||
anime.genre = document.select("div.SingleDetails li:contains(النوع) a,div.SingleDetails li:contains(الجودة) a").joinToString(", ") { it.text() }
|
anime.genre = document.select("div.SingleDetails li:contains(النوع) a,div.SingleDetails li:contains(الجودة) a").joinToString(", ") { it.text() }
|
||||||
anime.title = if (document.select("h2.postTitle").isNullOrEmpty())
|
anime.title = if (document.select("h2.postTitle").isNullOrEmpty()) {
|
||||||
titleEdit(document.select("div.H1Title h1").text()) else titleEdit(document.select("h2.postTitle").text())
|
titleEdit(document.select("div.H1Title h1").text())
|
||||||
|
} else {
|
||||||
|
titleEdit(document.select("h2.postTitle").text())
|
||||||
|
}
|
||||||
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
|
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
|
||||||
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
|
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
|
||||||
anime.status = SAnime.COMPLETED
|
anime.status = SAnime.COMPLETED
|
||||||
@ -307,7 +314,7 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
CatUnit("مسلسلات نتفلكس", "series/netflix-series"),
|
CatUnit("مسلسلات نتفلكس", "series/netflix-series"),
|
||||||
CatUnit("مسلسلات انمى", "series/anime-series"),
|
CatUnit("مسلسلات انمى", "series/anime-series"),
|
||||||
CatUnit("مسلسلات تركى", "series/turkish-series"),
|
CatUnit("مسلسلات تركى", "series/turkish-series"),
|
||||||
CatUnit("مسلسلات عربى", "series/arab-series")
|
CatUnit("مسلسلات عربى", "series/arab-series"),
|
||||||
)
|
)
|
||||||
// ============================= title edit =============================
|
// ============================= title edit =============================
|
||||||
|
|
||||||
@ -321,19 +328,25 @@ class Movizland : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
} else if (TitleRegex.SERIES_EPISODES.containsMatchIn(title)) {
|
} else if (TitleRegex.SERIES_EPISODES.containsMatchIn(title)) {
|
||||||
val newTitle = TitleRegex.SERIES_EPISODES.find(title)
|
val newTitle = TitleRegex.SERIES_EPISODES.find(title)
|
||||||
newTitle!!.groupValues[1].trim() + if (details) " (${newTitle.groupValues[2].trim()}ح)" else ""
|
newTitle!!.groupValues[1].trim() + if (details) " (${newTitle.groupValues[2].trim()}ح)" else ""
|
||||||
} else title
|
} else {
|
||||||
|
title
|
||||||
|
}
|
||||||
} else if (title.contains("فيلم")) {
|
} else if (title.contains("فيلم")) {
|
||||||
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) // افلام عربى
|
if (TitleRegex.ARABIC_MOVIE.containsMatchIn(title)) {
|
||||||
|
// افلام عربى
|
||||||
TitleRegex.ARABIC_MOVIE.find(title)!!.groupValues[1].trim() + if (details) " ($movie)" else ""
|
TitleRegex.ARABIC_MOVIE.find(title)!!.groupValues[1].trim() + if (details) " ($movie)" else ""
|
||||||
else if (TitleRegex.MOVIES.containsMatchIn(title))
|
} else if (TitleRegex.MOVIES.containsMatchIn(title)) {
|
||||||
TitleRegex.MOVIES.find(title)!!.groupValues[1].trim() + if (details) " (${TitleRegex.MOVIES.find(title)!!.groupValues[2].trim()})($movie)" else ""
|
TitleRegex.MOVIES.find(title)!!.groupValues[1].trim() + if (details) " (${TitleRegex.MOVIES.find(title)!!.groupValues[2].trim()})($movie)" else ""
|
||||||
else title
|
} else {
|
||||||
} else if (title.contains("انمي"))
|
title
|
||||||
|
}
|
||||||
|
} else if (title.contains("انمي")) {
|
||||||
Regex(if (title.contains("الموسم"))"انمي(.*)الموسم" else "انمي(.*)الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
|
Regex(if (title.contains("الموسم"))"انمي(.*)الموسم" else "انمي(.*)الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
|
||||||
else if (title.contains("برنامج"))
|
} else if (title.contains("برنامج")) {
|
||||||
Regex(if (title.contains("الموسم"))"برنامج(.*)الموسم" else "برنامج(.*)حلقة").find(title)!!.groupValues[1].removeSurrounding(" ال") + if (details) " (برنامج)" else ""
|
Regex(if (title.contains("الموسم"))"برنامج(.*)الموسم" else "برنامج(.*)حلقة").find(title)!!.groupValues[1].removeSurrounding(" ال") + if (details) " (برنامج)" else ""
|
||||||
else
|
} else {
|
||||||
title
|
title
|
||||||
|
}
|
||||||
return finalTitle.trim()
|
return finalTitle.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ class LinkboxExtractor(private val client: OkHttpClient) {
|
|||||||
Video(
|
Video(
|
||||||
it.jsonObject["url"].toString().replace("\"", ""),
|
it.jsonObject["url"].toString().replace("\"", ""),
|
||||||
"Linkbox: ${it.jsonObject["resolution"].toString().replace("\"", "")}",
|
"Linkbox: ${it.jsonObject["resolution"].toString().replace("\"", "")}",
|
||||||
it.jsonObject["url"].toString().replace("\"", "")
|
it.jsonObject["url"].toString().replace("\"", ""),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return videoList
|
return videoList
|
||||||
|
@ -9,7 +9,7 @@ import okhttp3.OkHttpClient
|
|||||||
class MoshahdaExtractor(private val client: OkHttpClient) {
|
class MoshahdaExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(link: String, headers: Headers): List<Video> {
|
fun videosFromUrl(link: String, headers: Headers): List<Video> {
|
||||||
val request = client.newCall(GET(link, headers)).execute().asJsoup()
|
val request = client.newCall(GET(link, headers)).execute().asJsoup()
|
||||||
val element = request.selectFirst("script:containsData(sources)")
|
val element = request.selectFirst("script:containsData(sources)")!!
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
|
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
|
||||||
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")
|
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class UQLoadExtractor(private val client: OkHttpClient) {
|
class UQLoadExtractor(private val client: OkHttpClient) {
|
||||||
fun videoFromUrl(url: String, quality: String): Video? {
|
fun videoFromUrl(url: String, quality: String): Video? {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val check = document.selectFirst("script:containsData(sources)").data()
|
val check = document.selectFirst("script:containsData(sources)")!!.data()
|
||||||
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
||||||
return if (check.contains("sources")) {
|
return if (check.contains("sources")) {
|
||||||
Video(url, quality, videoUrl)
|
Video(url, quality, videoUrl)
|
||||||
|
@ -73,14 +73,15 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
if (document.select(episodeListSelector()).isNullOrEmpty()) {
|
if (document.select(episodeListSelector()).isNullOrEmpty()) {
|
||||||
if (!document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").isNullOrEmpty()) {
|
if (!document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").isNullOrEmpty()) {
|
||||||
document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").map { episodes.add(newEpisodeFromElement(it, "mSeries")) }
|
document.select("mycima singlerelated.hasdivider ${popularAnimeSelector()}").map { episodes.add(newEpisodeFromElement(it, "mSeries")) }
|
||||||
} else
|
} else {
|
||||||
episodes.add(newEpisodeFromElement(document.select("div.Poster--Single-begin > a").first(), "movie"))
|
episodes.add(newEpisodeFromElement(document.selectFirst("div.Poster--Single-begin > a")!!, "movie"))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
document.select(episodeListSelector()).map { episodes.add(newEpisodeFromElement(it)) }
|
document.select(episodeListSelector()).map { episodes.add(newEpisodeFromElement(it)) }
|
||||||
document.select(seasonsNextPageSelector(seasonNumber)).firstOrNull()?.let {
|
document.selectFirst(seasonsNextPageSelector(seasonNumber))?.let {
|
||||||
seasonNumber++
|
seasonNumber++
|
||||||
addEpisodes(
|
addEpisodes(
|
||||||
client.newCall(GET(it.attr("abs:href"), headers)).execute().asJsoup()
|
client.newCall(GET(it.attr("abs:href"), headers)).execute().asJsoup(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,15 +94,16 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
val epNum = getNumberFromEpsString(element.text())
|
val epNum = getNumberFromEpsString(element.text())
|
||||||
episode.setUrlWithoutDomain(if (type == "mSeries") element.select("a").attr("href") else element.attr("abs:href"))
|
episode.setUrlWithoutDomain(if (type == "mSeries") element.select("a").attr("href") else element.attr("abs:href"))
|
||||||
if (type == "series")
|
if (type == "series") {
|
||||||
episode.episode_number = when {
|
episode.episode_number = when {
|
||||||
(epNum.isNotEmpty()) -> epNum.toFloat()
|
(epNum.isNotEmpty()) -> epNum.toFloat()
|
||||||
else -> 1F
|
else -> 1F
|
||||||
}
|
}
|
||||||
|
}
|
||||||
episode.name = when (type) {
|
episode.name = when (type) {
|
||||||
"movie" -> "مشاهدة"
|
"movie" -> "مشاهدة"
|
||||||
"mSeries" -> element.select("a").attr("title")
|
"mSeries" -> element.select("a").attr("title")
|
||||||
else -> element.ownerDocument().select("div.List--Seasons--Episodes a.selected").text() + element.text()
|
else -> element.ownerDocument()!!.select("div.List--Seasons--Episodes a.selected").text() + element.text()
|
||||||
}
|
}
|
||||||
return episode
|
return episode
|
||||||
}
|
}
|
||||||
@ -116,13 +118,13 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val iframe = document.selectFirst("iframe").attr("data-lazy-src")
|
val iframe = document.selectFirst("iframe")!!.attr("data-lazy-src")
|
||||||
val referer = response.request.url.encodedPath
|
val referer = response.request.url.encodedPath
|
||||||
val newHeaderList = mutableMapOf(Pair("referer", baseUrl + referer))
|
val newHeaderList = mutableMapOf(Pair("referer", baseUrl + referer))
|
||||||
headers.forEach { newHeaderList[it.first] = it.second }
|
headers.forEach { newHeaderList[it.first] = it.second }
|
||||||
val iframeResponse = client.newCall(GET(iframe, newHeaderList.toHeaders()))
|
val iframeResponse = client.newCall(GET(iframe, newHeaderList.toHeaders()))
|
||||||
.execute().asJsoup()
|
.execute().asJsoup()
|
||||||
return videosFromElement(iframeResponse.selectFirst(videoListSelector()))
|
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun videoListSelector() = "body"
|
override fun videoListSelector() = "body"
|
||||||
@ -143,7 +145,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
return videoList
|
return videoList
|
||||||
}
|
}
|
||||||
val sourceTag = element.ownerDocument().select("source").firstOrNull()!!
|
val sourceTag = element.ownerDocument()!!.select("source").firstOrNull()!!
|
||||||
return listOf(Video(sourceTag.attr("src"), "Default", sourceTag.attr("src")))
|
return listOf(Video(sourceTag.attr("src"), "Default", sourceTag.attr("src")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +258,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
SearchCategoryList(searchCategoryNames),
|
SearchCategoryList(searchCategoryNames),
|
||||||
AnimeFilter.Separator(),
|
AnimeFilter.Separator(),
|
||||||
AnimeFilter.Header("اقسام الموقع (تعمل فقط اذا كان البحث فارغ)"),
|
AnimeFilter.Header("اقسام الموقع (تعمل فقط اذا كان البحث فارغ)"),
|
||||||
CategoryList(categoryNames)
|
CategoryList(categoryNames),
|
||||||
)
|
)
|
||||||
|
|
||||||
private class SearchCategoryList(categories: Array<String>) : AnimeFilter.Select<String>("بحث عن", categories)
|
private class SearchCategoryList(categories: Array<String>) : AnimeFilter.Select<String>("بحث عن", categories)
|
||||||
@ -273,7 +275,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
CatUnit("فيلم", "/page/"),
|
CatUnit("فيلم", "/page/"),
|
||||||
CatUnit("مسلسل", "list/series/?page_number="),
|
CatUnit("مسلسل", "list/series/?page_number="),
|
||||||
CatUnit("انمى", "list/anime/?page_number="),
|
CatUnit("انمى", "list/anime/?page_number="),
|
||||||
CatUnit("برنامج", "list/tv/?page_number=")
|
CatUnit("برنامج", "list/tv/?page_number="),
|
||||||
)
|
)
|
||||||
private fun getCategoryList() = listOf(
|
private fun getCategoryList() = listOf(
|
||||||
CatUnit("اختر", ""),
|
CatUnit("اختر", ""),
|
||||||
|
@ -60,26 +60,30 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun titleEdit(title: String, details: Boolean = false): String {
|
private fun titleEdit(title: String, details: Boolean = false): String {
|
||||||
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title))
|
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title)) {
|
||||||
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + " (فيلم)" // افلام اجنبيه مترجمه
|
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + " (فيلم)" // افلام اجنبيه مترجمه
|
||||||
else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title))
|
} else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title)) {
|
||||||
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + " (مدبلج)(فيلم)" // افلام اجنبيه مدبلجه
|
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + " (مدبلج)(فيلم)" // افلام اجنبيه مدبلجه
|
||||||
else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) // افلام عربى
|
} else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) {
|
||||||
|
// افلام عربى
|
||||||
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + " (فيلم)"
|
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + " (فيلم)"
|
||||||
else if (title.contains("مسلسل")) {
|
} else if (title.contains("مسلسل")) {
|
||||||
if (title.contains("الموسم") and details) {
|
if (title.contains("الموسم") and details) {
|
||||||
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
|
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
|
||||||
return "${newTitle!!.groupValues[1]} (م.${newTitle.groupValues[2]})(${newTitle.groupValues[3]}ح)"
|
return "${newTitle!!.groupValues[1]} (م.${newTitle.groupValues[2]})(${newTitle.groupValues[3]}ح)"
|
||||||
} else if (title.contains("الحلقة") and details) {
|
} else if (title.contains("الحلقة") and details) {
|
||||||
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
|
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
|
||||||
return "${newTitle!!.groupValues[1]} (${newTitle.groupValues[2]}ح)"
|
return "${newTitle!!.groupValues[1]} (${newTitle.groupValues[2]}ح)"
|
||||||
} else Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
|
} else {
|
||||||
} else if (title.contains("انمي"))
|
Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
|
||||||
|
}
|
||||||
|
} else if (title.contains("انمي")) {
|
||||||
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + " (انمى)"
|
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + " (انمى)"
|
||||||
else if (title.contains("برنامج"))
|
} else if (title.contains("برنامج")) {
|
||||||
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + " (برنامج)"
|
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + " (برنامج)"
|
||||||
else
|
} else {
|
||||||
title
|
title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
|
override fun popularAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
|
||||||
@ -93,12 +97,13 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
fun addEpisodeNew(url: String, type: String, title: String = "") {
|
fun addEpisodeNew(url: String, type: String, title: String = "") {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.setUrlWithoutDomain(url)
|
episode.setUrlWithoutDomain(url)
|
||||||
if (type == "assembly")
|
if (type == "assembly") {
|
||||||
episode.name = title.replace("فيلم", "").trim()
|
episode.name = title.replace("فيلم", "").trim()
|
||||||
else if (type == "movie")
|
} else if (type == "movie") {
|
||||||
episode.name = "مشاهدة"
|
episode.name = "مشاهدة"
|
||||||
else
|
} else {
|
||||||
episode.name = title
|
episode.name = title
|
||||||
|
}
|
||||||
|
|
||||||
episodes.add(episode)
|
episodes.add(episode)
|
||||||
}
|
}
|
||||||
@ -110,7 +115,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
addEpisodeNew(
|
addEpisodeNew(
|
||||||
movie.select("a.fullClick").attr("href") + "watch/",
|
movie.select("a.fullClick").attr("href") + "watch/",
|
||||||
"assembly",
|
"assembly",
|
||||||
movie.select("h3").text()
|
movie.select("h3").text(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -129,7 +134,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
addEpisodeNew(
|
addEpisodeNew(
|
||||||
episode.attr("href"),
|
episode.attr("href"),
|
||||||
"series",
|
"series",
|
||||||
seasonNum + " " + episode.text()
|
seasonNum + " " + episode.text(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -142,7 +147,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
addEpisodeNew(
|
addEpisodeNew(
|
||||||
episode.attr("href"),
|
episode.attr("href"),
|
||||||
"series",
|
"series",
|
||||||
seasonNum + " " + episode.text()
|
seasonNum + " " + episode.text(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,18 +170,22 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val servers = document.select(videoListSelector())
|
val servers = document.select(videoListSelector())
|
||||||
fun getUrl(v_id: String, i: String): String {
|
fun getUrl(v_id: String, i: String): String {
|
||||||
val refererHeaders = Headers.headersOf(
|
val refererHeaders = Headers.headersOf(
|
||||||
"referer", response.request.url.toString(),
|
"referer",
|
||||||
"x-requested-with", "XMLHttpRequest",
|
response.request.url.toString(),
|
||||||
"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
|
"x-requested-with",
|
||||||
"user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42"
|
"XMLHttpRequest",
|
||||||
|
"Content-Type",
|
||||||
|
"application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
"user-agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42",
|
||||||
)
|
)
|
||||||
val requestBody = FormBody.Builder().add("id", v_id).add("i", i).build()
|
val requestBody = FormBody.Builder().add("id", v_id).add("i", i).build()
|
||||||
val iframe = client.newCall(
|
val iframe = client.newCall(
|
||||||
POST(
|
POST(
|
||||||
"$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php",
|
"$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php",
|
||||||
refererHeaders,
|
refererHeaders,
|
||||||
requestBody
|
requestBody,
|
||||||
)
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
return iframe.select("iframe").attr("src")
|
return iframe.select("iframe").attr("src")
|
||||||
}
|
}
|
||||||
@ -207,7 +216,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun videosFromElement(document: Document): List<Video> {
|
private fun videosFromElement(document: Document): List<Video> {
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
return try {
|
return try {
|
||||||
val scriptSelect = document.select("script:containsData(eval)").first().data()
|
val scriptSelect = document.selectFirst("script:containsData(eval)")!!.data()
|
||||||
val serverPrefix =
|
val serverPrefix =
|
||||||
scriptSelect.substringAfter("|net|cdn|amzn|").substringBefore("|rewind|icon|")
|
scriptSelect.substringAfter("|net|cdn|amzn|").substringBefore("|rewind|icon|")
|
||||||
val sourceServer = "https://$serverPrefix.e-amzn-cdn.net"
|
val sourceServer = "https://$serverPrefix.e-amzn-cdn.net"
|
||||||
@ -255,10 +264,11 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
var link = element.select("a.fullClick").attr("href")
|
var link = element.select("a.fullClick").attr("href")
|
||||||
anime.title = titleEdit(element.select("h3").text(), details = true).trim()
|
anime.title = titleEdit(element.select("h3").text(), details = true).trim()
|
||||||
if (link.contains("assemblies"))
|
if (link.contains("assemblies")) {
|
||||||
anime.thumbnail_url = element.select("a.image img").attr("data-src")
|
anime.thumbnail_url = element.select("a.image img").attr("data-src")
|
||||||
else
|
} else {
|
||||||
anime.thumbnail_url = element.select("a.image img.imgInit").attr("data-image")
|
anime.thumbnail_url = element.select("a.image img.imgInit").attr("data-image")
|
||||||
|
}
|
||||||
if (!link.contains("assemblies")) link += "watch/"
|
if (!link.contains("assemblies")) link += "watch/"
|
||||||
anime.setUrlWithoutDomain(link)
|
anime.setUrlWithoutDomain(link)
|
||||||
return anime
|
return anime
|
||||||
@ -300,7 +310,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
|
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
|
||||||
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
|
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
|
||||||
anime.status = SAnime.COMPLETED
|
anime.status = SAnime.COMPLETED
|
||||||
if (anime.title.contains("سلسلة")) anime.thumbnail_url = document.select("img.imgInit").first().attr("data-image")
|
if (anime.title.contains("سلسلة")) anime.thumbnail_url = document.selectFirst("img.imgInit")!!.attr("data-image")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +354,7 @@ class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
CatUnit("مسلسلات انمى", "category/مسلسلات-انمي-4/"),
|
CatUnit("مسلسلات انمى", "category/مسلسلات-انمي-4/"),
|
||||||
CatUnit("مسلسلات تركى", "category/مسلسلات-تركي-3/"),
|
CatUnit("مسلسلات تركى", "category/مسلسلات-تركي-3/"),
|
||||||
CatUnit("مسلسلات اسيوى", "category/مسلسلات-اسيوي/"),
|
CatUnit("مسلسلات اسيوى", "category/مسلسلات-اسيوي/"),
|
||||||
CatUnit("مسلسلات هندى", "category/مسلسلات-هندية/")
|
CatUnit("مسلسلات هندى", "category/مسلسلات-هندية/"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// preferred quality settings
|
// preferred quality settings
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class UQLoadExtractor(private val client: OkHttpClient) {
|
class UQLoadExtractor(private val client: OkHttpClient) {
|
||||||
fun videoFromUrl(url: String, quality: String): Video? {
|
fun videoFromUrl(url: String, quality: String): Video? {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val check = document.selectFirst("script:containsData(sources)").data()
|
val check = document.selectFirst("script:containsData(sources)")!!.data()
|
||||||
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
||||||
return if (check.contains("sources")) {
|
return if (check.contains("sources")) {
|
||||||
Video(url, quality, videoUrl)
|
Video(url, quality, videoUrl)
|
||||||
|
@ -8,15 +8,16 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
for (source in sources) {
|
for (source in sources) {
|
||||||
val src = source.substringBefore("\"")
|
val src = source.substringBefore("\"")
|
||||||
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
|
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
|
||||||
if (quality.length > 15)
|
if (quality.length > 15) {
|
||||||
quality = "Vidshare: 480p"
|
quality = "Vidshare: 480p"
|
||||||
|
}
|
||||||
val video = Video(src, quality, src)
|
val video = Video(src, quality, src)
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
}
|
}
|
||||||
|
@ -59,13 +59,16 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
private fun titleEdit(title: String, details: Boolean = false): String {
|
private fun titleEdit(title: String, details: Boolean = false): String {
|
||||||
return if (title.contains("فيلم")) {
|
return if (title.contains("فيلم")) {
|
||||||
if (Regex("فيلم (.*?) مترجم").containsMatchIn(title))
|
if (Regex("فيلم (.*?) مترجم").containsMatchIn(title)) {
|
||||||
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + if (details) " (فيلم)" else "" // افلام اجنبيه مترجمه
|
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + if (details) " (فيلم)" else "" // افلام اجنبيه مترجمه
|
||||||
else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title))
|
} else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title)) {
|
||||||
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + if (details) " (مدبلج)(فيلم)" else "" // افلام اجنبيه مدبلجه
|
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + if (details) " (مدبلج)(فيلم)" else "" // افلام اجنبيه مدبلجه
|
||||||
else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) // افلام عربى
|
} else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) {
|
||||||
|
// افلام عربى
|
||||||
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + if (details) " (فيلم)" else ""
|
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + if (details) " (فيلم)" else ""
|
||||||
else title
|
} else {
|
||||||
|
title
|
||||||
|
}
|
||||||
} else if (title.contains("مسلسل")) {
|
} else if (title.contains("مسلسل")) {
|
||||||
if (title.contains("الموسم")) {
|
if (title.contains("الموسم")) {
|
||||||
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
|
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
|
||||||
@ -73,13 +76,16 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
} else if (title.contains("الحلقة")) {
|
} else if (title.contains("الحلقة")) {
|
||||||
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
|
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
|
||||||
newTitle!!.groupValues[1] + if (details) " (${newTitle.groupValues[2]}ح)" else ""
|
newTitle!!.groupValues[1] + if (details) " (${newTitle.groupValues[2]}ح)" else ""
|
||||||
} else Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (مسلسل)" else ""
|
} else {
|
||||||
} else if (title.contains("انمي"))
|
Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (مسلسل)" else ""
|
||||||
|
}
|
||||||
|
} else if (title.contains("انمي")) {
|
||||||
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
|
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (انمى)" else ""
|
||||||
else if (title.contains("برنامج"))
|
} else if (title.contains("برنامج")) {
|
||||||
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (برنامج)" else ""
|
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + if (details) " (برنامج)" else ""
|
||||||
else
|
} else {
|
||||||
title
|
title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeNextPageSelector(): String = "div.pagination ul.page-numbers li a.next"
|
override fun popularAnimeNextPageSelector(): String = "div.pagination ul.page-numbers li a.next"
|
||||||
@ -99,22 +105,26 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
fun addEpisodes(response: Response) {
|
fun addEpisodes(response: Response) {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val url = response.request.url.toString()
|
val url = response.request.url.toString()
|
||||||
if (document.select(seasonsNextPageSelector()).isNullOrEmpty())
|
if (document.select(seasonsNextPageSelector()).isNullOrEmpty()) {
|
||||||
addEpisodeNew(url, "مشاهدة")
|
addEpisodeNew(url, "مشاهدة")
|
||||||
else
|
} else {
|
||||||
document.select(seasonsNextPageSelector()).reversed().forEach { season ->
|
document.select(seasonsNextPageSelector()).reversed().forEach { season ->
|
||||||
val seasonNum = season.select("h3").text()
|
val seasonNum = season.select("h3").text()
|
||||||
(
|
(
|
||||||
if (seasonNum == document.selectFirst("div#mpbreadcrumbs a span:contains(الموسم)").text())
|
if (seasonNum == document.selectFirst("div#mpbreadcrumbs a span:contains(الموسم)")!!.text()) {
|
||||||
document else client.newCall(GET(season.selectFirst("a").attr("href"))).execute().asJsoup()
|
document
|
||||||
|
} else {
|
||||||
|
client.newCall(GET(season.selectFirst("a")!!.attr("href"))).execute().asJsoup()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.select("section.allepcont a").forEach { episode ->
|
.select("section.allepcont a").forEach { episode ->
|
||||||
addEpisodeNew(
|
addEpisodeNew(
|
||||||
episode.attr("href") + "watch/",
|
episode.attr("href") + "watch/",
|
||||||
seasonNum + " : الحلقة " + episode.select("div.epnum").text().filter { it.isDigit() }
|
seasonNum + " : الحلقة " + episode.select("div.epnum").text().filter { it.isDigit() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addEpisodes(response)
|
addEpisodes(response)
|
||||||
return episodes
|
return episodes
|
||||||
@ -207,7 +217,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.title = titleEdit(element.select("h3").text(), true).trim()
|
anime.title = titleEdit(element.select("h3").text(), true).trim()
|
||||||
anime.thumbnail_url = element.select("img").attr(if (element.ownerDocument().location().contains("?s="))"src" else "data-src")
|
anime.thumbnail_url = element.select("img").attr(if (element.ownerDocument()!!.location().contains("?s="))"src" else "data-src")
|
||||||
anime.setUrlWithoutDomain(element.select("a").attr("href") + "watch/")
|
anime.setUrlWithoutDomain(element.select("a").attr("href") + "watch/")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
@ -266,7 +276,7 @@ class Tuktukcinema : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
CatUnit("مسلسلات انمى", "category/anime-6/انمي-مترجم/"),
|
CatUnit("مسلسلات انمى", "category/anime-6/انمي-مترجم/"),
|
||||||
CatUnit("مسلسلات تركى", "category/series-9/مسلسلات-تركي/"),
|
CatUnit("مسلسلات تركى", "category/series-9/مسلسلات-تركي/"),
|
||||||
CatUnit("مسلسلات اسيوى", "category/series-9/مسلسلات-أسيوي/"),
|
CatUnit("مسلسلات اسيوى", "category/series-9/مسلسلات-أسيوي/"),
|
||||||
CatUnit("مسلسلات هندى", "category/series-9/مسلسلات-هندي/")
|
CatUnit("مسلسلات هندى", "category/series-9/مسلسلات-هندي/"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// preferred quality settings
|
// preferred quality settings
|
||||||
|
@ -9,7 +9,7 @@ import okhttp3.OkHttpClient
|
|||||||
class MoshahdaExtractor(private val client: OkHttpClient) {
|
class MoshahdaExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(link: String, headers: Headers): List<Video> {
|
fun videosFromUrl(link: String, headers: Headers): List<Video> {
|
||||||
val request = client.newCall(GET(link, headers)).execute().asJsoup()
|
val request = client.newCall(GET(link, headers)).execute().asJsoup()
|
||||||
val element = request.selectFirst("script:containsData(sources)")
|
val element = request.selectFirst("script:containsData(sources)")!!
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
|
val qualityMap = mapOf("l" to "240p", "n" to "360p", "h" to "480p", "x" to "720p", "o" to "1080p")
|
||||||
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")
|
val data2 = element.data().substringAfter(", file: \"").substringBefore("\"}],")
|
||||||
|
@ -8,11 +8,12 @@ import okhttp3.OkHttpClient
|
|||||||
class UQLoadExtractor(private val client: OkHttpClient) {
|
class UQLoadExtractor(private val client: OkHttpClient) {
|
||||||
fun videoFromUrl(url: String, quality: String): Video? {
|
fun videoFromUrl(url: String, quality: String): Video? {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val check = document.selectFirst("script:containsData(sources)").data()
|
val check = document.selectFirst("script:containsData(sources)")!!.data()
|
||||||
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
|
||||||
return if (check.contains("sources"))
|
return if (check.contains("sources")) {
|
||||||
Video(url, quality, videoUrl)
|
Video(url, quality, videoUrl)
|
||||||
else
|
} else {
|
||||||
null
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,16 @@ import okhttp3.OkHttpClient
|
|||||||
class VidBomExtractor(private val client: OkHttpClient) {
|
class VidBomExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String): List<Video> {
|
fun videosFromUrl(url: String): List<Video> {
|
||||||
val doc = client.newCall(GET(url)).execute().asJsoup()
|
val doc = client.newCall(GET(url)).execute().asJsoup()
|
||||||
val script = doc.selectFirst("script:containsData(sources)")
|
val script = doc.selectFirst("script:containsData(sources)")!!
|
||||||
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
val data = script.data().substringAfter("sources: [").substringBefore("],")
|
||||||
val sources = data.split("file:\"").drop(1)
|
val sources = data.split("file:\"").drop(1)
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
for (source in sources) {
|
for (source in sources) {
|
||||||
val src = source.substringBefore("\"")
|
val src = source.substringBefore("\"")
|
||||||
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
|
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
|
||||||
if (quality.length > 15)
|
if (quality.length > 15) {
|
||||||
quality = "Vidshare: 480p"
|
quality = "Vidshare: 480p"
|
||||||
|
}
|
||||||
val video = Video(src, quality, src)
|
val video = Video(src, quality, src)
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
||||||
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
||||||
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
|
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +136,9 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
url.contains("dood") -> {
|
url.contains("dood") -> {
|
||||||
val video = DoodExtractor(client).videoFromUrl(url, "Dood mirror")
|
val video = DoodExtractor(client).videoFromUrl(url, "Dood mirror")
|
||||||
if (video != null)
|
if (video != null) {
|
||||||
videoList.add(video)
|
videoList.add(video)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
url.contains("4shared") -> {
|
url.contains("4shared") -> {
|
||||||
val video = SharedExtractor(client).videosFromUrl(url)
|
val video = SharedExtractor(client).videosFromUrl(url)
|
||||||
@ -182,7 +183,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
||||||
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
||||||
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
|
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,11 +197,12 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
val doc = if (!document.select("div.anime-page-link").isNullOrEmpty())
|
val doc = if (!document.select("div.anime-page-link").isNullOrEmpty()) {
|
||||||
client.newCall(GET(document.select("div.anime-page-link a").attr("href"), headers)).execute().asJsoup()
|
client.newCall(GET(document.select("div.anime-page-link a").attr("href"), headers)).execute().asJsoup()
|
||||||
else
|
} else {
|
||||||
document
|
document
|
||||||
anime.thumbnail_url = doc.select("img.thumbnail").first().attr("src")
|
}
|
||||||
|
anime.thumbnail_url = doc.selectFirst("img.thumbnail")!!.attr("src")
|
||||||
anime.title = doc.select("h1.anime-details-title").text()
|
anime.title = doc.select("h1.anime-details-title").text()
|
||||||
anime.genre = doc.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
|
anime.genre = doc.select("ul.anime-genres > li > a, div.anime-info > a").joinToString(", ") { it.text() }
|
||||||
anime.description = doc.select("p.anime-story").text()
|
anime.description = doc.select("p.anime-story").text()
|
||||||
@ -227,7 +229,7 @@ class WitAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
anime.setUrlWithoutDomain(element.select("div.anime-card-poster a").attr("href"))
|
||||||
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
anime.title = element.select("div.anime-card-poster div.ehover6 img").attr("alt")
|
||||||
anime.thumbnail_url = element.select("div.anime-card-poster div.ehover6 img").first().attr("abs:src")
|
anime.thumbnail_url = element.selectFirst("div.anime-card-poster div.ehover6 img")!!.attr("abs:src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
|||||||
class SharedExtractor(private val client: OkHttpClient) {
|
class SharedExtractor(private val client: OkHttpClient) {
|
||||||
fun videosFromUrl(url: String, quality: String = "mirror"): Video? {
|
fun videosFromUrl(url: String, quality: String = "mirror"): Video? {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
return document.select("source").firstOrNull()?.let {
|
return document.selectFirst("source")?.let {
|
||||||
Video(it.attr("src"), "4Shared: $quality", it.attr("src"))
|
Video(it.attr("src"), "4Shared: $quality", it.attr("src"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
|
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
|
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +122,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
is GenreFilter -> url.addQueryParameter("genre", filter.toUriPart())
|
is GenreFilter -> url.addQueryParameter("genre", filter.toUriPart())
|
||||||
is StatusFilter -> url.addQueryParameter("status", filter.toUriPart())
|
is StatusFilter -> url.addQueryParameter("status", filter.toUriPart())
|
||||||
// is LetterFilter -> url.addQueryParameter("letter", filter.toUriPart())
|
// is LetterFilter -> url.addQueryParameter("letter", filter.toUriPart())
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GET(url.build().toString(), headers)
|
GET(url.build().toString(), headers)
|
||||||
@ -132,7 +133,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.thumbnail_url = document.select("div.inner--image img").first().attr("src")
|
anime.thumbnail_url = document.selectFirst("div.inner--image img")!!.attr("src")
|
||||||
anime.title = document.select("h1.post--inner-title").text()
|
anime.title = document.select("h1.post--inner-title").text()
|
||||||
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
|
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
|
||||||
anime.description = document.select("div.post--content--inner").text()
|
anime.description = document.select("div.post--content--inner").text()
|
||||||
@ -164,7 +165,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
private fun getStatusFilters(): Array<Pair<String?, String>> = arrayOf(
|
private fun getStatusFilters(): Array<Pair<String?, String>> = arrayOf(
|
||||||
Pair("", "<اختر>"),
|
Pair("", "<اختر>"),
|
||||||
Pair("مستمر", "مستمر"),
|
Pair("مستمر", "مستمر"),
|
||||||
Pair("منتهي", "منتهي")
|
Pair("منتهي", "منتهي"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getGenreFilters(): Array<Pair<String?, String>> = arrayOf(
|
private fun getGenreFilters(): Array<Pair<String?, String>> = arrayOf(
|
||||||
@ -187,7 +188,7 @@ class XsAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
Pair("مدرسي", "مدرسي"),
|
Pair("مدرسي", "مدرسي"),
|
||||||
Pair("مغامرات", "مغامرات"),
|
Pair("مغامرات", "مغامرات"),
|
||||||
Pair("موسيقي", "موسيقي"),
|
Pair("موسيقي", "موسيقي"),
|
||||||
Pair("نفسي", "نفسي")
|
Pair("نفسي", "نفسي"),
|
||||||
)
|
)
|
||||||
|
|
||||||
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
|
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String?, String>>) :
|
||||||
|
@ -47,7 +47,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
|
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.setUrlWithoutDomain(element.attr("href"))
|
anime.setUrlWithoutDomain(element.attr("href"))
|
||||||
anime.title = element.attr("title")
|
anime.title = element.attr("title")
|
||||||
anime.thumbnail_url = element.select("div.itemtype_anime_poster img").first().attr("data-src")
|
anime.thumbnail_url = element.selectFirst("div.itemtype_anime_poster img")!!.attr("data-src")
|
||||||
return anime
|
return anime
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ class XsMovie : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun animeDetailsParse(document: Document): SAnime {
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
anime.thumbnail_url = document.select("div.inner--image img").first().attr("src")
|
anime.thumbnail_url = document.selectFirst("div.inner--image img")!!.attr("src")
|
||||||
anime.title = document.select("h1.post--inner-title").text()
|
anime.title = document.select("h1.post--inner-title").text()
|
||||||
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
|
anime.genre = document.select("ul.terms--and--metas > li:contains(تصنيفات الأنمي) > a").joinToString(", ") { it.text() }
|
||||||
anime.description = document.select("div.post--content--inner").text()
|
anime.description = document.select("div.post--content--inner").text()
|
||||||
|
@ -72,7 +72,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body!!.string())
|
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body.string())
|
||||||
val newAnime = SAnime.create().apply {
|
val newAnime = SAnime.create().apply {
|
||||||
title = anime.name!!
|
title = anime.name!!
|
||||||
setUrlWithoutDomain("$baseUrl/api/show/" + anime.url!!)
|
setUrlWithoutDomain("$baseUrl/api/show/" + anime.url!!)
|
||||||
@ -95,7 +95,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun popularAnimeParse(response: Response) = parseAnimePage(response)
|
override fun popularAnimeParse(response: Response) = parseAnimePage(response)
|
||||||
|
|
||||||
private fun parseAnimePage(response: Response, singlePage: Boolean = false): AnimesPage {
|
private fun parseAnimePage(response: Response, singlePage: Boolean = false): AnimesPage {
|
||||||
val animes = json.decodeFromString(ListSerializer(AnimeDto.serializer()), response.body!!.string())
|
val animes = json.decodeFromString(ListSerializer(AnimeDto.serializer()), response.body.string())
|
||||||
if (animes.isEmpty()) return AnimesPage(emptyList(), false)
|
if (animes.isEmpty()) return AnimesPage(emptyList(), false)
|
||||||
val animeList = mutableListOf<SAnime>()
|
val animeList = mutableListOf<SAnime>()
|
||||||
for (anime in animes) {
|
for (anime in animes) {
|
||||||
@ -124,7 +124,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/show/airing/${page - 1}", refererHeader)
|
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/show/airing/${page - 1}", refererHeader)
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): AnimesPage {
|
override fun latestUpdatesParse(response: Response): AnimesPage {
|
||||||
val releases = json.decodeFromString(ListSerializer(Release.serializer()), response.body!!.string()).toMutableList()
|
val releases = json.decodeFromString(ListSerializer(Release.serializer()), response.body.string()).toMutableList()
|
||||||
if (releases.isEmpty()) return AnimesPage(emptyList(), false)
|
if (releases.isEmpty()) return AnimesPage(emptyList(), false)
|
||||||
val animeList = mutableListOf<SAnime>()
|
val animeList = mutableListOf<SAnime>()
|
||||||
val releaseList = mutableListOf<Int>()
|
val releaseList = mutableListOf<Int>()
|
||||||
@ -153,13 +153,13 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = POST(
|
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = POST(
|
||||||
url = "$baseUrl/api/show/search",
|
url = "$baseUrl/api/show/search",
|
||||||
headers = refererHeader,
|
headers = refererHeader,
|
||||||
body = "{\"search\":\"$query\"}".toRequestBody("application/json".toMediaType())
|
body = "{\"search\":\"$query\"}".toRequestBody("application/json".toMediaType()),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun searchAnimeParse(response: Response) = parseAnimePage(response, singlePage = true)
|
override fun searchAnimeParse(response: Response) = parseAnimePage(response, singlePage = true)
|
||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body!!.string())
|
val anime = json.decodeFromString(AnimeDetailsDto.serializer(), response.body.string())
|
||||||
if (anime.seasons.isNullOrEmpty()) return emptyList()
|
if (anime.seasons.isNullOrEmpty()) return emptyList()
|
||||||
val episodeList = mutableListOf<SEpisode>()
|
val episodeList = mutableListOf<SEpisode>()
|
||||||
val animeUrl = anime.url!!
|
val animeUrl = anime.url!!
|
||||||
@ -170,8 +170,8 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val seasonPart = json.decodeFromString(
|
val seasonPart = json.decodeFromString(
|
||||||
Season.serializer(),
|
Season.serializer(),
|
||||||
client.newCall(
|
client.newCall(
|
||||||
GET("$baseUrl/api/show/$animeUrl/${season.id!!}/$page")
|
GET("$baseUrl/api/show/$animeUrl/${season.id!!}/$page"),
|
||||||
).execute().body!!.string()
|
).execute().body.string(),
|
||||||
)
|
)
|
||||||
page++
|
page++
|
||||||
episodes.addAll(seasonPart.episodes!!)
|
episodes.addAll(seasonPart.episodes!!)
|
||||||
@ -189,7 +189,7 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val streams = json.decodeFromString(Episode.serializer(), response.body!!.string()).streams
|
val streams = json.decodeFromString(Episode.serializer(), response.body.string()).streams
|
||||||
if (streams.isNullOrEmpty()) return emptyList()
|
if (streams.isNullOrEmpty()) return emptyList()
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
for (stream in streams) {
|
for (stream in streams) {
|
||||||
@ -236,20 +236,26 @@ class Aniflix : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
otherList.add(video)
|
otherList.add(video)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else otherList += this
|
} else {
|
||||||
|
otherList += this
|
||||||
|
}
|
||||||
val newList = mutableListOf<Video>()
|
val newList = mutableListOf<Video>()
|
||||||
var preferred = 0
|
var preferred = 0
|
||||||
for (video in hosterList) {
|
for (video in hosterList) {
|
||||||
if (video.quality.contains(subPreference)) {
|
if (video.quality.contains(subPreference)) {
|
||||||
newList.add(preferred, video)
|
newList.add(preferred, video)
|
||||||
preferred++
|
preferred++
|
||||||
} else newList.add(video)
|
} else {
|
||||||
|
newList.add(video)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (video in otherList) {
|
for (video in otherList) {
|
||||||
if (video.quality.contains(subPreference)) {
|
if (video.quality.contains(subPreference)) {
|
||||||
newList.add(preferred, video)
|
newList.add(preferred, video)
|
||||||
preferred++
|
preferred++
|
||||||
} else newList.add(video)
|
} else {
|
||||||
|
newList.add(video)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return newList
|
return newList
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ data class AnimeDto(
|
|||||||
@SerialName("name")
|
@SerialName("name")
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
@SerialName("url")
|
@SerialName("url")
|
||||||
val url: String? = null
|
val url: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
object IntSerializer : JsonTransformingSerializer<Int>(Int.serializer()) {
|
object IntSerializer : JsonTransformingSerializer<Int>(Int.serializer()) {
|
||||||
@ -57,7 +57,7 @@ data class AnimeDetailsDto(
|
|||||||
@SerialName("url")
|
@SerialName("url")
|
||||||
val url: String? = null,
|
val url: String? = null,
|
||||||
@SerialName("seasons")
|
@SerialName("seasons")
|
||||||
val seasons: List<Season>? = null
|
val seasons: List<Season>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -69,7 +69,7 @@ data class Season(
|
|||||||
@SerialName("id")
|
@SerialName("id")
|
||||||
val id: Int? = null,
|
val id: Int? = null,
|
||||||
@SerialName("length")
|
@SerialName("length")
|
||||||
val length: Int? = null
|
val length: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -81,19 +81,19 @@ data class Episode(
|
|||||||
@SerialName("name")
|
@SerialName("name")
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
@SerialName("streams")
|
@SerialName("streams")
|
||||||
val streams: List<Stream>? = null
|
val streams: List<Stream>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Release(
|
data class Release(
|
||||||
@SerialName("season")
|
@SerialName("season")
|
||||||
val season: ShortSeason? = null
|
val season: ShortSeason? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ShortSeason(
|
data class ShortSeason(
|
||||||
@SerialName("show")
|
@SerialName("show")
|
||||||
val anime: AnimeDto? = null
|
val anime: AnimeDto? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -103,13 +103,13 @@ data class Stream(
|
|||||||
@SerialName("lang")
|
@SerialName("lang")
|
||||||
val lang: String? = null,
|
val lang: String? = null,
|
||||||
@SerialName("hoster")
|
@SerialName("hoster")
|
||||||
val hoster: Hoster? = null
|
val hoster: Hoster? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Hoster(
|
data class Hoster(
|
||||||
@SerialName("name")
|
@SerialName("name")
|
||||||
val name: String? = null
|
val name: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -117,5 +117,5 @@ data class Genre(
|
|||||||
@SerialName("name")
|
@SerialName("name")
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
@SerialName("id")
|
@SerialName("id")
|
||||||
val id: Int? = null
|
val id: Int? = null,
|
||||||
)
|
)
|
||||||
|
@ -16,10 +16,10 @@ class StreamlareExtractor(private val client: OkHttpClient) {
|
|||||||
POST(
|
POST(
|
||||||
"https://slwatch.co/api/video/stream/get",
|
"https://slwatch.co/api/video/stream/get",
|
||||||
body = "{\"id\":\"$id\"}"
|
body = "{\"id\":\"$id\"}"
|
||||||
.toRequestBody("application/json".toMediaType())
|
.toRequestBody("application/json".toMediaType()),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.execute().body!!.string()
|
.execute().body.string()
|
||||||
|
|
||||||
playlist.substringAfter("\"label\":\"").split("\"label\":\"").forEach {
|
playlist.substringAfter("\"label\":\"").split("\"label\":\"").forEach {
|
||||||
val quality = it.substringAfter("\"label\":\"").substringBefore("\",") + ", ${stream.lang}"
|
val quality = it.substringAfter("\"label\":\"").substringBefore("\",") + ", ${stream.lang}"
|
||||||
|
@ -70,8 +70,8 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
GET(
|
GET(
|
||||||
"$baseUrl${anime.url}",
|
"$baseUrl${anime.url}",
|
||||||
headers =
|
headers =
|
||||||
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
|
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.execute().request.headers
|
.execute().request.headers
|
||||||
return GET("$baseUrl${anime.url}", headers = headers)
|
return GET("$baseUrl${anime.url}", headers = headers)
|
||||||
@ -283,8 +283,8 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
GET(
|
GET(
|
||||||
"$baseUrl${anime.url}",
|
"$baseUrl${anime.url}",
|
||||||
headers =
|
headers =
|
||||||
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
|
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.execute().request.headers
|
.execute().request.headers
|
||||||
return GET("$baseUrl${anime.url}", headers = headers)
|
return GET("$baseUrl${anime.url}", headers = headers)
|
||||||
|
@ -13,8 +13,8 @@ class StreamZExtractor(private val client: OkHttpClient) {
|
|||||||
val videoUrl = client.newCall(
|
val videoUrl = client.newCall(
|
||||||
GET(
|
GET(
|
||||||
"https://get.streamz.tw/getlink-$dllpart.dll",
|
"https://get.streamz.tw/getlink-$dllpart.dll",
|
||||||
headers = Headers.headersOf("referer", "https://streamz.ws/", "accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "range", "bytes=0-")
|
headers = Headers.headersOf("referer", "https://streamz.ws/", "accept", "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "range", "bytes=0-"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.execute().request.url.toString()
|
.execute().request.url.toString()
|
||||||
return Video(url, quality, videoUrl)
|
return Video(url, quality, videoUrl)
|
||||||
|
@ -73,7 +73,7 @@ class AnimeBase : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
} else {
|
} else {
|
||||||
"data-dubbed=\"0\""
|
"data-dubbed=\"0\""
|
||||||
}
|
}
|
||||||
}][data-hoster=\"1\"], div.tab-content #specials div.panel button[data-dubbed=\"0\"][data-hoster=\"1\"]"
|
}][data-hoster=\"1\"], div.tab-content #specials div.panel button[data-dubbed=\"0\"][data-hoster=\"1\"]",
|
||||||
)
|
)
|
||||||
episodeElement.forEach {
|
episodeElement.forEach {
|
||||||
val episode = episodeFromElement(it)
|
val episode = episodeFromElement(it)
|
||||||
|
@ -137,9 +137,16 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
|
"X-Requested-With",
|
||||||
)
|
"XMLHttpRequest",
|
||||||
)
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"application/json, text/javascript, */*; q=0.01",
|
||||||
|
"cache-control",
|
||||||
|
"max-age=15",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
|
|
||||||
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
|
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
|
||||||
@ -156,11 +163,15 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
GET(
|
GET(
|
||||||
"$baseUrl/files/captcha?cid=0&hash=$hash",
|
"$baseUrl/files/captcha?cid=0&hash=$hash",
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Referer", url.replace("#$id$epnum", ""),
|
"Referer",
|
||||||
"Accept", "image/avif,image/webp,*/*", "cache-control", "max-age=15"
|
url.replace("#$id$epnum", ""),
|
||||||
)
|
"Accept",
|
||||||
)
|
"image/avif,image/webp,*/*",
|
||||||
).execute().body?.byteString()
|
"cache-control",
|
||||||
|
"max-age=15",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).execute().body.byteString()
|
||||||
val size = png.toString()
|
val size = png.toString()
|
||||||
.substringAfter("[size=").substringBefore(" hex")
|
.substringAfter("[size=").substringBefore(" hex")
|
||||||
pnglist.add("$size | $hash")
|
pnglist.add("$size | $hash")
|
||||||
@ -194,9 +205,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15"
|
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).execute()
|
).execute()
|
||||||
val maxdoc = client.newCall(
|
val maxdoc = client.newCall(
|
||||||
POST(
|
POST(
|
||||||
@ -204,9 +215,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
||||||
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
|
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).execute().asJsoup().toString()
|
).execute().asJsoup().toString()
|
||||||
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
||||||
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
||||||
@ -277,19 +288,32 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*"
|
"Origin",
|
||||||
)
|
baseUrl,
|
||||||
)
|
"X-Requested-With",
|
||||||
|
"XMLHttpRequest",
|
||||||
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"*/*",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute()
|
).execute()
|
||||||
val mindoc = client.newCall(
|
val mindoc = client.newCall(
|
||||||
POST(
|
POST(
|
||||||
"$baseUrl/ajax/captcha",
|
"$baseUrl/ajax/captcha",
|
||||||
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
"Origin",
|
||||||
"Accept", "application/json, text/javascript, */*; q=0.01"
|
baseUrl,
|
||||||
)
|
"X-Requested-With",
|
||||||
)
|
"XMLHttpRequest",
|
||||||
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"application/json, text/javascript, */*; q=0.01",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute().asJsoup().toString()
|
).execute().asJsoup().toString()
|
||||||
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
||||||
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
||||||
@ -367,9 +391,16 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&rT=1".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
|
"X-Requested-With",
|
||||||
)
|
"XMLHttpRequest",
|
||||||
)
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"application/json, text/javascript, */*; q=0.01",
|
||||||
|
"cache-control",
|
||||||
|
"max-age=15",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute().asJsoup()
|
).execute().asJsoup()
|
||||||
|
|
||||||
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
|
val hashes = capfiles.toString().substringAfter("[").substringBefore("]").split(",")
|
||||||
@ -386,11 +417,15 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
GET(
|
GET(
|
||||||
"$baseUrl/files/captcha?cid=0&hash=$hash",
|
"$baseUrl/files/captcha?cid=0&hash=$hash",
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Referer", url.replace("#$id$epnum", ""),
|
"Referer",
|
||||||
"Accept", "image/avif,image/webp,*/*", "cache-control", "max-age=15"
|
url.replace("#$id$epnum", ""),
|
||||||
)
|
"Accept",
|
||||||
)
|
"image/avif,image/webp,*/*",
|
||||||
).execute().body?.byteString()
|
"cache-control",
|
||||||
|
"max-age=15",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).execute().body.byteString()
|
||||||
val size = png.toString()
|
val size = png.toString()
|
||||||
.substringAfter("[size=").substringBefore(" hex")
|
.substringAfter("[size=").substringBefore(" hex")
|
||||||
pnglist.add("$size | $hash")
|
pnglist.add("$size | $hash")
|
||||||
@ -424,9 +459,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&pC=$maxhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15"
|
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*", "cache-control", "max-age=15",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).execute()
|
).execute()
|
||||||
val maxdoc = client.newCall(
|
val maxdoc = client.newCall(
|
||||||
POST(
|
POST(
|
||||||
@ -434,9 +469,9 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$maxhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
||||||
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15"
|
"Accept", "application/json, text/javascript, */*; q=0.01", "cache-control", "max-age=15",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).execute().asJsoup().toString()
|
).execute().asJsoup().toString()
|
||||||
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
if (maxdoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
||||||
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
||||||
@ -507,19 +542,32 @@ class AnimeLoads : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
"$baseUrl/files/captcha",
|
"$baseUrl/files/captcha",
|
||||||
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "cID=0&pC=$minhash&rT=2".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""), "Accept", "*/*"
|
"Origin",
|
||||||
)
|
baseUrl,
|
||||||
)
|
"X-Requested-With",
|
||||||
|
"XMLHttpRequest",
|
||||||
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"*/*",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute()
|
).execute()
|
||||||
val mindoc = client.newCall(
|
val mindoc = client.newCall(
|
||||||
POST(
|
POST(
|
||||||
"$baseUrl/ajax/captcha",
|
"$baseUrl/ajax/captcha",
|
||||||
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
body = "enc=${enc.replace("=", "%3D")}&response=captcha&captcha-idhf=0&captcha-hf=$minhash".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
|
||||||
headers = Headers.headersOf(
|
headers = Headers.headersOf(
|
||||||
"Origin", baseUrl, "X-Requested-With", "XMLHttpRequest", "Referer", url.replace("#$id$epnum", ""),
|
"Origin",
|
||||||
"Accept", "application/json, text/javascript, */*; q=0.01"
|
baseUrl,
|
||||||
)
|
"X-Requested-With",
|
||||||
)
|
"XMLHttpRequest",
|
||||||
|
"Referer",
|
||||||
|
url.replace("#$id$epnum", ""),
|
||||||
|
"Accept",
|
||||||
|
"application/json, text/javascript, */*; q=0.01",
|
||||||
|
),
|
||||||
|
),
|
||||||
).execute().asJsoup().toString()
|
).execute().asJsoup().toString()
|
||||||
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
if (mindoc.substringAfter("\"code\":\"").substringBefore("\",").contains("error")) {
|
||||||
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
throw Exception("Captcha bypass failed! Clear Cookies & Webview data. Or wait some time.")
|
||||||
|
@ -58,7 +58,7 @@ class DdosGuardInterceptor(private val client: OkHttpClient) : Interceptor {
|
|||||||
return ddg2Cookie
|
return ddg2Cookie
|
||||||
}
|
}
|
||||||
val wellKnown = client.newCall(GET("https://check.ddos-guard.net/check.js"))
|
val wellKnown = client.newCall(GET("https://check.ddos-guard.net/check.js"))
|
||||||
.execute().body!!.string()
|
.execute().body.string()
|
||||||
.substringAfter("'", "")
|
.substringAfter("'", "")
|
||||||
.substringBefore("'", "")
|
.substringBefore("'", "")
|
||||||
val checkUrl = "${url.scheme}://${url.host + wellKnown}"
|
val checkUrl = "${url.scheme}://${url.host + wellKnown}"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user